diff options
| author | Chris Heyes <rumcode@icloud.com> | 2019-11-01 19:18:33 +0000 |
|---|---|---|
| committer | Chris Heyes <rumcode@icloud.com> | 2019-11-01 19:18:33 +0000 |
| commit | 153e7525b5d64f12deb318d50aee1d9dbeb1fc8e (patch) | |
| tree | b8ac88c0788af31bcb3c5041d78306c2120714f6 /src | |
| parent | d85893954dccc7833e3d954db641d9fd2a3c7451 (diff) | |
| parent | 44a303e5778fb8564964d53523634f34f8589489 (diff) | |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src')
| -rw-r--r-- | src/build_settings.cpp | 56 | ||||
| -rw-r--r-- | src/check_decl.cpp | 247 | ||||
| -rw-r--r-- | src/check_expr.cpp | 1163 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 221 | ||||
| -rw-r--r-- | src/check_type.cpp | 400 | ||||
| -rw-r--r-- | src/checker.cpp | 166 | ||||
| -rw-r--r-- | src/checker.hpp | 220 | ||||
| -rw-r--r-- | src/checker_builtin_procs.hpp | 320 | ||||
| -rw-r--r-- | src/common.cpp | 8 | ||||
| -rw-r--r-- | src/entity.cpp | 13 | ||||
| -rw-r--r-- | src/exact_value.cpp | 264 | ||||
| -rw-r--r-- | src/gb/gb.h | 30 | ||||
| -rw-r--r-- | src/ir.cpp | 963 | ||||
| -rw-r--r-- | src/ir_print.cpp | 300 | ||||
| -rw-r--r-- | src/main.cpp | 115 | ||||
| -rw-r--r-- | src/parser.cpp | 524 | ||||
| -rw-r--r-- | src/parser.hpp | 60 | ||||
| -rw-r--r-- | src/priority_queue.cpp | 4 | ||||
| -rw-r--r-- | src/range_cache.cpp | 70 | ||||
| -rw-r--r-- | src/string.cpp | 86 | ||||
| -rw-r--r-- | src/thread_pool.cpp | 184 | ||||
| -rw-r--r-- | src/tokenizer.cpp | 14 | ||||
| -rw-r--r-- | src/types.cpp | 272 | ||||
| -rw-r--r-- | src/unicode.cpp | 1 | ||||
| -rw-r--r-- | src/utf8proc/utf8proc.c | 1 |
25 files changed, 4533 insertions, 1169 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 992443f7c..5d1c07a67 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -2,7 +2,7 @@ enum TargetOsKind { TargetOs_Invalid, TargetOs_windows, - TargetOs_osx, + TargetOs_darwin, TargetOs_linux, TargetOs_essence, @@ -30,7 +30,7 @@ enum TargetEndianKind { String target_os_names[TargetOs_COUNT] = { str_lit(""), str_lit("windows"), - str_lit("osx"), + str_lit("darwin"), str_lit("linux"), str_lit("essence"), }; @@ -55,9 +55,7 @@ TargetEndianKind target_endians[TargetArch_COUNT] = { -String const ODIN_VERSION = str_lit("0.10.1"); -String cross_compile_target = str_lit(""); -String cross_compile_lib_dir = str_lit(""); +String const ODIN_VERSION = str_lit("0.10.2"); @@ -66,6 +64,7 @@ struct TargetMetrics { TargetArchKind arch; isize word_size; isize max_align; + String target_triplet; }; @@ -109,6 +108,7 @@ struct BuildContext { bool has_resource; String opt_flags; String llc_flags; + String target_triplet; String link_flags; bool is_dll; bool generate_docs; @@ -121,6 +121,7 @@ struct BuildContext { bool no_crt; bool use_lld; bool vet; + bool cross_compiling; QueryDataSetSettings query_data_set_settings; @@ -135,18 +136,19 @@ struct BuildContext { gb_global BuildContext build_context = {0}; - gb_global TargetMetrics target_windows_386 = { TargetOs_windows, TargetArch_386, 4, 8, + str_lit("i686-pc-windows"), }; gb_global TargetMetrics target_windows_amd64 = { TargetOs_windows, TargetArch_amd64, 8, 16, + str_lit("x86_64-pc-windows-gnu"), }; gb_global TargetMetrics target_linux_386 = { @@ -154,23 +156,47 @@ gb_global TargetMetrics target_linux_386 = { TargetArch_386, 4, 8, + str_lit("i686-pc-linux-gnu"), }; gb_global TargetMetrics target_linux_amd64 = { TargetOs_linux, TargetArch_amd64, 8, 16, + str_lit("x86_64-pc-linux-gnu"), +}; + +gb_global TargetMetrics target_darwin_amd64 = { + TargetOs_darwin, + TargetArch_amd64, + 8, + 16, + str_lit("x86_64-apple-darwin"), }; -gb_global TargetMetrics target_osx_amd64 = { - TargetOs_osx, +gb_global TargetMetrics target_essence_amd64 = { + TargetOs_essence, TargetArch_amd64, 8, 16, + str_lit("x86_64-pc-none-elf"), }; +struct NamedTargetMetrics { + String name; + TargetMetrics *metrics; +}; +gb_global NamedTargetMetrics named_targets[] = { + { str_lit("essence_amd64"), &target_essence_amd64 }, + { str_lit("darwin_amd64"), &target_darwin_amd64 }, + { str_lit("linux_386"), &target_linux_386 }, + { str_lit("linux_amd64"), &target_linux_amd64 }, + { str_lit("windows_386"), &target_windows_386 }, + { str_lit("windows_amd64"), &target_windows_amd64 }, +}; +NamedTargetMetrics *selected_target_metrics; TargetOsKind get_target_os_from_string(String str) { for (isize i = 0; i < TargetOs_COUNT; i++) { @@ -522,7 +548,7 @@ String get_fullpath_core(gbAllocator a, String path) { -void init_build_context(void) { +void init_build_context(TargetMetrics *cross_target) { BuildContext *bc = &build_context; gb_affinity_init(&bc->affinity); @@ -540,7 +566,7 @@ void init_build_context(void) { #if defined(GB_SYSTEM_WINDOWS) metrics = target_windows_amd64; #elif defined(GB_SYSTEM_OSX) - metrics = target_osx_amd64; + metrics = target_darwin_amd64; #else metrics = target_linux_amd64; #endif @@ -554,8 +580,9 @@ void init_build_context(void) { #endif #endif - if (cross_compile_target.len) { - bc->ODIN_OS = cross_compile_target; + if (cross_target) { + metrics = *cross_target; + bc->cross_compiling = true; } GB_ASSERT(metrics.os != TargetOs_Invalid); @@ -573,6 +600,7 @@ void init_build_context(void) { bc->max_align = metrics.max_align; bc->link_flags = str_lit(" "); bc->opt_flags = str_lit(" "); + bc->target_triplet = metrics.target_triplet; gbString llc_flags = gb_string_make_reserve(heap_allocator(), 64); @@ -590,7 +618,7 @@ void init_build_context(void) { case TargetOs_windows: bc->link_flags = str_lit("/machine:x64 "); break; - case TargetOs_osx: + case TargetOs_darwin: break; case TargetOs_linux: bc->link_flags = str_lit("-arch x86-64 "); @@ -603,7 +631,7 @@ void init_build_context(void) { case TargetOs_windows: bc->link_flags = str_lit("/machine:x86 "); break; - case TargetOs_osx: + case TargetOs_darwin: gb_printf_err("Unsupported architecture\n"); gb_exit(1); break; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1a6fad918..ebfc29899 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -41,11 +41,20 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri } if (operand->mode == Addressing_Type) { - gbString t = type_to_string(operand->type); - error(operand->expr, "Cannot assign a type '%s' to variable '%.*s'", t, LIT(e->token.string)); - gb_string_free(t); - e->type = operand->type; - return nullptr; + if (e->type != nullptr && is_type_typeid(e->type)) { + add_type_info_type(ctx, operand->type); + add_type_and_value(ctx->info, operand->expr, Addressing_Value, e->type, exact_value_typeid(operand->type)); + return e->type; + } else { + gbString t = type_to_string(operand->type); + defer (gb_string_free(t)); + error(operand->expr, "Cannot assign a type '%s' to variable '%.*s'", t, LIT(e->token.string)); + if (e->type == nullptr) { + error_line("\tThe type of the variable '%.*s' cannot be inferred as a type does not have a type\n", LIT(e->token.string)); + } + e->type = operand->type; + return nullptr; + } } @@ -112,7 +121,8 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Ar isize rhs_count = operands.count; for_array(i, operands) { if (operands[i].mode == Addressing_Invalid) { - rhs_count--; + // TODO(bill): Should I ignore invalid parameters? + // rhs_count--; } } @@ -239,7 +249,7 @@ isize total_attribute_count(DeclInfo *decl) { } -void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) { +void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) { GB_ASSERT(e->type == nullptr); DeclInfo *decl = decl_info_of_entity(e); @@ -247,9 +257,8 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) check_decl_attributes(ctx, decl->attributes, const_decl_attribute, nullptr); } - - bool is_distinct = is_type_distinct(type_expr); - Ast *te = remove_type_alias_clutter(type_expr); + bool is_distinct = is_type_distinct(init_expr); + Ast *te = remove_type_alias_clutter(init_expr); e->type = t_invalid; String name = e->token.string; Type *named = alloc_type_named(name, nullptr, e); @@ -265,7 +274,7 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) named->Named.base = base_type(bt); if (is_distinct && is_type_typeid(e->type)) { - error(type_expr, "'distinct' cannot be applied to 'typeid'"); + error(init_expr, "'distinct' cannot be applied to 'typeid'"); is_distinct = false; } if (!is_distinct) { @@ -274,6 +283,19 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) e->TypeName.is_type_alias = true; } + + if (decl->type_expr != nullptr) { + Type *t = check_type(ctx, decl->type_expr); + if (t != nullptr && !is_type_typeid(t)) { + Operand operand = {}; + operand.mode = Addressing_Type; + operand.type = e->type; + operand.expr = init_expr; + check_assignment(ctx, &operand, t, str_lit("constant declaration")); + } + } + + // using decl if (decl->is_using) { // NOTE(bill): Must be an enum declaration @@ -362,15 +384,14 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, switch (operand.mode) { case Addressing_Type: { + if (e->type != nullptr && !is_type_typeid(e->type)) { + check_assignment(ctx, &operand, e->type, str_lit("constant declaration")); + } + e->kind = Entity_TypeName; e->type = nullptr; - DeclInfo *d = ctx->decl; - if (d->type_expr != nullptr) { - error(e->token, "A type declaration cannot have an type parameter"); - } - d->type_expr = d->init_expr; - check_type_decl(ctx, e, d->type_expr, named_type); + check_type_decl(ctx, e, ctx->decl->init_expr, named_type); return; } @@ -654,7 +675,6 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { bool is_foreign = e->Procedure.is_foreign; bool is_export = e->Procedure.is_export; - bool is_require_results = (pl->tags & ProcTag_require_results) != 0; if (e->pkg != nullptr && e->token.string == "main") { if (pt->param_count != 0 || @@ -714,10 +734,10 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } } - if (pt->result_count == 0 && is_require_results) { - error(pl->type, "'#require_results' is not needed on a procedure with no results"); + if (pt->result_count == 0 && ac.require_results) { + error(pl->type, "'require_results' is not needed on a procedure with no results"); } else { - pt->require_results = is_require_results; + pt->require_results = ac.require_results; } if (ac.link_name.len > 0) { @@ -791,7 +811,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } } -void check_var_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) { +void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) { GB_ASSERT(e->type == nullptr); GB_ASSERT(e->kind == Entity_Variable); @@ -805,6 +825,7 @@ void check_var_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_ex ac.init_expr_list_count = init_expr != nullptr ? 1 : 0; DeclInfo *decl = decl_info_of_entity(e); + GB_ASSERT(decl == ctx->decl); if (decl != nullptr) { check_decl_attributes(ctx, decl->attributes, var_decl_attribute, &ac); } @@ -936,7 +957,6 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) ptr_set_destroy(&entity_set); - for_array(j, pge->entities) { Entity *p = pge->entities[j]; if (p->type == t_invalid) { @@ -962,27 +982,40 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) defer (end_error_block()); ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type); - switch (kind) { + bool both_have_where_clauses = false; + if (p->decl_info->proc_lit != nullptr && q->decl_info->proc_lit != nullptr) { + GB_ASSERT(p->decl_info->proc_lit->kind == Ast_ProcLit); + GB_ASSERT(q->decl_info->proc_lit->kind == Ast_ProcLit); + auto pl = &p->decl_info->proc_lit->ProcLit; + auto ql = &q->decl_info->proc_lit->ProcLit; + + // Allow collisions if the procedures both have 'where' clauses and are both polymorphic + bool pw = pl->where_token.kind != Token_Invalid && is_type_polymorphic(p->type, true); + bool qw = ql->where_token.kind != Token_Invalid && is_type_polymorphic(q->type, true); + both_have_where_clauses = pw && qw; + } + + if (!both_have_where_clauses) switch (kind) { case ProcOverload_Identical: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; // case ProcOverload_CallingConvention: - // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name)); + // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); // is_invalid = true; // break; case ProcOverload_ParamVariadic: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_ResultCount: case ProcOverload_ResultTypes: - error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in this scope", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_Polymorphic: #if 0 - error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in this scope which is not allowed", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in the procedure group '%.*s' which is not allowed", LIT(name), LIT(proc_group_name)); is_invalid = true; #endif break; @@ -1051,13 +1084,13 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_ switch (e->kind) { case Entity_Variable: - check_var_decl(&c, e, d->type_expr, d->init_expr); + check_global_variable_decl(&c, e, d->type_expr, d->init_expr); break; case Entity_Constant: check_const_decl(&c, e, d->type_expr, d->init_expr, named_type); break; case Entity_TypeName: { - check_type_decl(&c, e, d->type_expr, named_type); + check_type_decl(&c, e, d->init_expr, named_type); break; } case Entity_Procedure: @@ -1074,6 +1107,11 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_ } +struct ProcUsingVar { + Entity *e; + Entity *uvar; +}; + void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *type, Ast *body) { if (body == nullptr) { @@ -1098,76 +1136,117 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty ctx->curr_proc_decl = decl; ctx->curr_proc_sig = type; - GB_ASSERT(type->kind == Type_Proc); - if (type->Proc.param_count > 0) { - TypeTuple *params = &type->Proc.params->Tuple; - for_array(i, params->variables) { - Entity *e = params->variables[i]; - if (e->kind != Entity_Variable) { - continue; - } - if (!(e->flags & EntityFlag_Using)) { - continue; - } - bool is_immutable = e->Variable.is_immutable; - bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type); - String name = e->token.string; - Type *t = base_type(type_deref(e->type)); - if (t->kind == Type_Struct) { - Scope *scope = t->Struct.scope; - if (scope == nullptr) { - scope = scope_of_node(t->Struct.node); + ast_node(bs, BlockStmt, body); + + Array<ProcUsingVar> using_entities = {}; + using_entities.allocator = heap_allocator(); + defer (array_free(&using_entities)); + + { + GB_ASSERT(type->kind == Type_Proc); + if (type->Proc.param_count > 0) { + TypeTuple *params = &type->Proc.params->Tuple; + for_array(i, params->variables) { + Entity *e = params->variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + if (!(e->flags & EntityFlag_Using)) { + continue; } - GB_ASSERT(scope != nullptr); - for_array(i, scope->elements.entries) { - Entity *f = scope->elements.entries[i].value; - if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); - uvar->Variable.is_immutable = is_immutable; - if (is_value) uvar->flags |= EntityFlag_Value; - - Entity *prev = scope_insert(ctx->scope, uvar); - if (prev != nullptr) { - error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(name), LIT(prev->token.string)); - break; + bool is_immutable = e->Variable.is_immutable; + bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type); + String name = e->token.string; + Type *t = base_type(type_deref(e->type)); + if (t->kind == Type_Struct) { + Scope *scope = t->Struct.scope; + if (scope == nullptr) { + scope = scope_of_node(t->Struct.node); + } + GB_ASSERT(scope != nullptr); + for_array(i, scope->elements.entries) { + Entity *f = scope->elements.entries[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr); + uvar->Variable.is_immutable = is_immutable; + if (is_value) uvar->flags |= EntityFlag_Value; + + ProcUsingVar puv = {e, uvar}; + array_add(&using_entities, puv); + } } + } else { + error(e->token, "'using' can only be applied to variables of type struct"); + break; } - } else { - error(e->token, "'using' can only be applied to variables of type struct"); - break; } } } - ast_node(bs, BlockStmt, body); - // check_open_scope(ctx, body); - check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls); - if (type->Proc.result_count > 0) { - if (!check_is_terminating(body)) { - if (token.kind == Token_Ident) { - error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string)); - } else { - // NOTE(bill): Anonymous procedure (lambda) - error(bs->close, "Missing return statement at the end of the procedure"); + + for_array(i, using_entities) { + Entity *e = using_entities[i].e; + Entity *uvar = using_entities[i].uvar; + Entity *prev = scope_insert(ctx->scope, uvar); + if (prev != nullptr) { + error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string)); + break; + } + } + + + bool where_clause_ok = evaluate_where_clauses(ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + if (!where_clause_ok) { + // NOTE(bill, 2019-08-31): Don't check the body as the where clauses failed + return; + } + + check_open_scope(ctx, body); + { + for_array(i, using_entities) { + Entity *e = using_entities[i].e; + Entity *uvar = using_entities[i].uvar; + Entity *prev = scope_insert(ctx->scope, uvar); + // NOTE(bill): Don't err here + } + + check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls); + + if (type->Proc.result_count > 0) { + if (!check_is_terminating(body)) { + if (token.kind == Token_Ident) { + error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string)); + } else { + // NOTE(bill): Anonymous procedure (lambda) + error(bs->close, "Missing return statement at the end of the procedure"); + } } } } - // check_close_scope(ctx); + check_close_scope(ctx); check_scope_usage(ctx->checker, ctx->scope); +#if 1 if (decl->parent != nullptr) { - // NOTE(bill): Add the dependencies from the procedure literal (lambda) - for_array(i, decl->deps.entries) { - Entity *e = decl->deps.entries[i].ptr; - ptr_set_add(&decl->parent->deps, e); - } - for_array(i, decl->type_info_deps.entries) { - Type *t = decl->type_info_deps.entries[i].ptr; - ptr_set_add(&decl->parent->type_info_deps, t); + Scope *ps = decl->parent->scope; + if (ps->flags & (ScopeFlag_File & ScopeFlag_Pkg & ScopeFlag_Global)) { + return; + } else { + // NOTE(bill): Add the dependencies from the procedure literal (lambda) + // But only at the procedure level + for_array(i, decl->deps.entries) { + Entity *e = decl->deps.entries[i].ptr; + ptr_set_add(&decl->parent->deps, e); + } + for_array(i, decl->type_info_deps.entries) { + Type *t = decl->type_info_deps.entries[i].ptr; + ptr_set_add(&decl->parent->type_info_deps, t); + } } } +#endif } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4c5ca2348..fae38fa01 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -62,7 +62,7 @@ Type * make_optional_ok_type (Type *value); void check_type_decl (CheckerContext *c, Entity *e, Ast *type_expr, Type *def); Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint); Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name); -Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> ordered_operands); +Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure); void check_not_tuple (CheckerContext *c, Operand *operand); void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type); gbString expr_to_string (Ast *expression); @@ -89,8 +89,8 @@ Type * check_init_variable (CheckerContext *c, Entity *e, Operand * Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type); -void set_procedure_abi_types(CheckerContext *c, Type *type); - +void set_procedure_abi_types(gbAllocator a, Type *type); +void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); Entity *entity_from_expr(Ast *expr) { expr = unparen_expr(expr); @@ -159,7 +159,7 @@ isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0 src = base_type(src); if (!is_type_struct(src)) { - return false; + return 0; } for_array(i, src->Struct.fields) { @@ -442,6 +442,10 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type } if (operand->mode == Addressing_Type) { + if (is_type_typeid(type)) { + add_type_info_type(c, operand->type); + return 4; + } return -1; } @@ -497,6 +501,14 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type if (is_type_complex(dst)) { return 1; } + if (is_type_quaternion(dst)) { + return 2; + } + break; + case Basic_UntypedQuaternion: + if (is_type_quaternion(dst)) { + return 1; + } break; } } @@ -664,17 +676,6 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co return; } - #if 0 - if (operand->mode == Addressing_Type) { - Type *t = base_type(type); - if (t->kind == Type_Pointer && - t->Pointer.elem == t_type_info) { - add_type_info_type(c, type); - return; - } - } - #endif - if (is_type_untyped(operand->type)) { Type *target_type = type; if (type == nullptr || is_type_any(type)) { @@ -747,7 +748,12 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co return; } - if (!check_is_assignable_to(c, operand, type)) { + if (check_is_assignable_to(c, operand, type)) { + if (operand->mode == Addressing_Type && is_type_typeid(type)) { + add_type_info_type(c, operand->type); + add_type_and_value(c->info, operand->expr, Addressing_Value, type, exact_value_typeid(operand->type)); + } + } else { gbString expr_str = expr_to_string(operand->expr); gbString op_type_str = type_to_string(operand->type); gbString type_str = type_to_string(type); @@ -778,6 +784,7 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co op_type_str, type_str, LIT(context_name)); + check_assignment_error_suggestion(c, operand, type); break; } operand->mode = Addressing_Invalid; @@ -956,7 +963,7 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, } if (modify_type) { - set_procedure_abi_types(c, source); + set_procedure_abi_types(c->allocator, source); } return true; @@ -1438,7 +1445,7 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ ExactValue imag = exact_value_imag(v); if (real.kind != ExactValue_Invalid && imag.kind != ExactValue_Invalid) { - if (out_value) *out_value = exact_binary_operator_value(Token_Add, real, exact_value_make_imag(imag)); + if (out_value) *out_value = exact_value_complex(exact_value_to_f64(real), exact_value_to_f64(imag)); return true; } break; @@ -1450,6 +1457,36 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ } return false; + } else if (is_type_quaternion(type)) { + ExactValue v = exact_value_to_quaternion(in_value); + if (v.kind != ExactValue_Quaternion) { + return false; + } + + switch (type->Basic.kind) { + case Basic_quaternion128: + case Basic_quaternion256: { + ExactValue real = exact_value_real(v); + ExactValue imag = exact_value_imag(v); + ExactValue jmag = exact_value_jmag(v); + ExactValue kmag = exact_value_kmag(v); + if (real.kind != ExactValue_Invalid && + imag.kind != ExactValue_Invalid) { + if (out_value) *out_value = exact_value_quaternion(exact_value_to_f64(real), exact_value_to_f64(imag), exact_value_to_f64(jmag), exact_value_to_f64(kmag)); + return true; + } + break; + } + case Basic_UntypedComplex: + if (out_value) *out_value = exact_value_to_quaternion(*out_value); + return true; + case Basic_UntypedQuaternion: + return true; + + default: GB_PANIC("Compiler error: Unknown complex type!"); break; + } + + return false; } else if (is_type_pointer(type)) { if (in_value.kind == ExactValue_Pointer) { return true; @@ -1471,28 +1508,97 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ return false; } + +void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) { + gbString a = expr_to_string(o->expr); + gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + ); + + Type *src = base_type(o->type); + Type *dst = base_type(type); + + if (is_type_array(src) && is_type_slice(dst)) { + Type *s = src->Array.elem; + Type *d = dst->Slice.elem; + if (are_types_identical(s, d)) { + error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a); + } + } else if (are_types_identical(src, dst)) { + error_line("\tSuggestion: the expression may be directly casted to type %s\n", b); + } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { + error_line("\tSuggestion: a string may be casted to %s\n", a, b); + } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { + error_line("\tSuggestion: the expression may be casted to %s\n", b); + } +} + +void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) { + gbString a = expr_to_string(o->expr); + gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + ); + + Type *src = base_type(o->type); + Type *dst = base_type(type); + + if (is_type_array(src) && is_type_slice(dst)) { + Type *s = src->Array.elem; + Type *d = dst->Slice.elem; + if (are_types_identical(s, d)) { + error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a); + } + } else if (is_type_pointer(o->type) && is_type_integer(type)) { + if (is_type_uintptr(type)) { + error_line("\tSuggestion: a pointer may be directly casted to %s\n", b); + } else { + error_line("\tSuggestion: for a pointer to be casted to an integer, it must be converted to 'uintptr' first\n"); + i64 x = type_size_of(o->type); + i64 y = type_size_of(type); + if (x != y) { + error_line("\tNote: the type of expression and the type of the cast have a different size in bytes, %lld vs %lld\n", x, y); + } + } + } else if (is_type_integer(o->type) && is_type_pointer(type)) { + if (is_type_uintptr(o->type)) { + error_line("\tSuggestion: %a may be directly casted to %s\n", a, b); + } else { + error_line("\tSuggestion: for an integer to be casted to a pointer, it must be converted to 'uintptr' first\n"); + } + } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { + error_line("\tSuggestion: a string may be casted to %s\n", a, b); + } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { + error_line("\tSuggestion: the expression may be casted to %s\n", b); + } +} + + void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { - GB_ASSERT(is_type_constant_type(type)); GB_ASSERT(o->mode == Addressing_Constant); - if (!check_representable_as_constant(c, o->value, type, &o->value)) { + if (!is_type_constant_type(type) || !check_representable_as_constant(c, o->value, type, &o->value)) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + o->mode = Addressing_Invalid; + ); + if (is_type_numeric(o->type) && is_type_numeric(type)) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { - gbAllocator ha = heap_allocator(); - String str = big_int_to_string(ha, &o->value.value_integer); - defer (gb_free(ha, str.text)); - error(o->expr, "'%s = %.*s' overflows '%s'", a, LIT(str), b); + error(o->expr, "Cannot convert '%s' to '%s'", a, b); + check_assignment_error_suggestion(c, o, type); } } else { error(o->expr, "Cannot convert '%s' to '%s'", a, b); + check_assignment_error_suggestion(c, o, type); } - - gb_string_free(b); - gb_string_free(a); - o->mode = Addressing_Invalid; } } @@ -1623,6 +1729,23 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { return; } + if (x->mode == Addressing_Type && is_type_typeid(y->type)) { + add_type_info_type(c, x->type); + add_type_and_value(c->info, x->expr, Addressing_Value, y->type, exact_value_typeid(x->type)); + + x->mode = Addressing_Value; + x->type = t_untyped_bool; + return; + } else if (is_type_typeid(x->type) && y->mode == Addressing_Type) { + add_type_info_type(c, y->type); + add_type_and_value(c->info, y->expr, Addressing_Value, x->type, exact_value_typeid(y->type)); + + x->mode = Addressing_Value; + x->type = t_untyped_bool; + return; + } + + gbString err_str = nullptr; defer (if (err_str != nullptr) { @@ -1759,6 +1882,21 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } break; } + } else if (is_type_quaternion(x->type) || is_type_quaternion(y->type)) { + switch (op) { + case Token_CmpEq: + switch (8*size) { + case 128: add_package_dependency(c, "runtime", "quaternion128_eq"); break; + case 256: add_package_dependency(c, "runtime", "quaternion256_eq"); break; + } + break; + case Token_NotEq: + switch (8*size) { + case 128: add_package_dependency(c, "runtime", "quaternion128_ne"); break; + case 256: add_package_dependency(c, "runtime", "quaternion256_ne"); break; + } + break; + } } } @@ -1767,7 +1905,7 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } -void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) { +void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *type_hint) { GB_ASSERT(node->kind == Ast_BinaryExpr); ast_node(be, BinaryExpr, node); @@ -1845,6 +1983,9 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) { info->is_lhs = true; } x->mode = Addressing_Value; + if (type_hint && is_type_integer(type_hint)) { + x->type = type_hint; + } // x->value = x_val; return; } @@ -1975,6 +2116,14 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return true; } + if (is_type_complex(src) && is_type_quaternion(dst)) { + return true; + } + + if (is_type_quaternion(src) && is_type_quaternion(dst)) { + return true; + } + if (is_type_bit_field_value(src) && is_type_integer(dst)) { return true; } @@ -2097,6 +2246,8 @@ void check_cast(CheckerContext *c, Operand *x, Type *type) { gb_string_free(to_type); gb_string_free(expr_str); + check_cast_error_suggestion(c, x, type); + x->mode = Addressing_Invalid; return; } @@ -2167,7 +2318,7 @@ bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x, Operand *y } -void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as_type_hint=false) { +void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint, bool use_lhs_as_type_hint=false) { GB_ASSERT(node->kind == Ast_BinaryExpr); Operand y_ = {}, *y = &y_; @@ -2178,15 +2329,23 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as case Token_CmpEq: case Token_NotEq: { // NOTE(bill): Allow comparisons between types - check_expr_or_type(c, x, be->left); + check_expr_or_type(c, x, be->left, type_hint); check_expr_or_type(c, y, be->right, x->type); bool xt = x->mode == Addressing_Type; bool yt = y->mode == Addressing_Type; // If only one is a type, this is an error if (xt ^ yt) { GB_ASSERT(xt != yt); - if (xt) error_operand_not_expression(x); - if (yt) error_operand_not_expression(y); + if (xt) { + if (!is_type_typeid(y->type)) { + error_operand_not_expression(x); + } + } + if (yt) { + if (!is_type_typeid(x->type)) { + error_operand_not_expression(y); + } + } } break; @@ -2278,11 +2437,11 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as return; default: - check_expr(c, x, be->left); + check_expr_with_type_hint(c, x, be->left, type_hint); if (use_lhs_as_type_hint) { check_expr_with_type_hint(c, y, be->right, x->type); } else { - check_expr(c, y, be->right); + check_expr_with_type_hint(c, y, be->right, type_hint); } break; } @@ -2307,7 +2466,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as } if (token_is_shift(op.kind)) { - check_shift(c, x, y, node); + check_shift(c, x, y, node, type_hint); return; } @@ -2554,6 +2713,8 @@ ExactValue convert_exact_value_for_type(ExactValue v, Type *type) { v = exact_value_to_integer(v); } else if (is_type_complex(t)) { v = exact_value_to_complex(v); + } else if (is_type_quaternion(t)) { + v = exact_value_to_quaternion(v); } return v; } @@ -2649,6 +2810,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { case Basic_UntypedInteger: case Basic_UntypedFloat: case Basic_UntypedComplex: + case Basic_UntypedQuaternion: case Basic_UntypedRune: if (!is_type_numeric(target_type)) { operand->mode = Addressing_Invalid; @@ -2895,7 +3057,6 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } if (selector->kind != Ast_Ident) { - // if (selector->kind != Ast_Ident) { error(selector, "Illegal selector kind: '%.*s'", LIT(ast_strings[selector->kind])); operand->mode = Addressing_Invalid; operand->expr = node; @@ -3131,8 +3292,59 @@ bool check_identifier_exists(Scope *s, Ast *node, bool nested = false, Scope **o return false; } +typedef bool (BuiltinTypeIsProc)(Type *t); + +BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_end - BuiltinProc__type_begin - 1] = { + nullptr, // BuiltinProc_type_base_type + nullptr, // BuiltinProc_type_core_type + nullptr, // BuiltinProc_type_elem_type + + is_type_boolean, + is_type_integer, + is_type_rune, + is_type_float, + is_type_complex, + is_type_quaternion, + is_type_string, + is_type_typeid, + is_type_any, + is_type_endian_little, + is_type_endian_big, + is_type_numeric, + is_type_ordered, + is_type_ordered_numeric, + is_type_indexable, + is_type_sliceable, + is_type_simple_compare, + is_type_dereferenceable, + is_type_valid_for_keys, + + is_type_named, + is_type_pointer, + is_type_opaque, + is_type_array, + is_type_slice, + is_type_dynamic_array, + + is_type_map, + is_type_struct, + is_type_union, + is_type_enum, + is_type_proc, + is_type_bit_field, + is_type_bit_field_value, + is_type_bit_set, + is_type_simd_vector, + + type_has_nil, + + nullptr, // BuiltinProc_type_proc_parameter_count + nullptr, // BuiltinProc_type_proc_return_count +}; + -bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id) { + +bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { ast_node(ce, CallExpr, call); if (ce->inlining != ProcInlining_none) { error(call, "Inlining operators are not allowed on built-in procedures"); @@ -3178,7 +3390,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 /*fallthrough*/ } default: - if (ce->args.count > 0) { + if (BuiltinProc__type_begin < id && id < BuiltinProc__type_end) { + check_expr_or_type(c, operand, ce->args[0]); + } else if (ce->args.count > 0) { check_multi_expr(c, operand, ce->args[0]); } break; @@ -3306,19 +3520,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (!operand->value.value_bool) { gbString arg = expr_to_string(ce->args[0]); error(call, "Compile time assertion: %s", arg); + if (c->proc_name != "") { + gbString str = type_to_string(c->curr_proc_sig); + error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); + gb_string_free(str); + } gb_string_free(arg); } operand->type = t_untyped_bool; operand->mode = Addressing_Constant; + } else if (name == "panic") { + if (ce->args.count != 1) { + error(call, "'#panic' expects 1 argument, got %td", ce->args.count); + return false; + } + if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) { + gbString str = expr_to_string(ce->args[0]); + error(call, "'%s' is not a constant string", str); + gb_string_free(str); + return false; + } + error(call, "Compile time panic: %.*s", LIT(operand->value.value_string)); + if (c->proc_name != "") { + gbString str = type_to_string(c->curr_proc_sig); + error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); + gb_string_free(str); + } + operand->type = t_invalid; + operand->mode = Addressing_NoValue; } else if (name == "defined") { if (ce->args.count != 1) { error(call, "'#defined' expects 1 argument, got %td", ce->args.count); return false; } Ast *arg = unparen_expr(ce->args[0]); - if (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr) { - error(call, "'#defined' expects an identifier or selector expression, got %s", LIT(ast_strings[arg->kind])); + if (arg == nullptr || (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr)) { + error(call, "'#defined' expects an identifier or selector expression, got %.*s", LIT(ast_strings[arg->kind])); return false; } @@ -3644,6 +3882,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } operand->mode = Addressing_Value; + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + break; } @@ -3690,7 +3932,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } if (x.mode == Addressing_Constant && y.mode == Addressing_Constant) { - operand->value = exact_binary_operator_value(Token_Add, x.value, y.value); + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + operand->value = exact_value_complex(r, i); operand->mode = Addressing_Constant; } else { operand->mode = Addressing_Value; @@ -3705,13 +3949,109 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 default: GB_PANIC("Invalid type"); break; } + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_quaternion: { + // quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type + Operand x = *operand; + Operand y = {}; + Operand z = {}; + Operand w = {}; + + // NOTE(bill): Invalid will be the default till fixed + operand->type = t_invalid; + operand->mode = Addressing_Invalid; + + check_expr(c, &y, ce->args[1]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &z, ce->args[2]); + if (y.mode == Addressing_Invalid) { + return false; + } + check_expr(c, &w, ce->args[3]); + if (y.mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false; + convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false; + convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false; + convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false; + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant && + z.mode == Addressing_Constant && + w.mode == Addressing_Constant) { + if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) { + x.type = t_untyped_float; + } + if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) { + y.type = t_untyped_float; + } + if (is_type_numeric(z.type) && exact_value_imag(z.value).value_float == 0) { + z.type = t_untyped_float; + } + if (is_type_numeric(w.type) && exact_value_imag(w.value).value_float == 0) { + w.type = t_untyped_float; + } + } + + if (!(are_types_identical(x.type, y.type) && are_types_identical(x.type, z.type) && are_types_identical(x.type, w.type))) { + gbString tx = type_to_string(x.type); + gbString ty = type_to_string(y.type); + gbString tz = type_to_string(z.type); + gbString tw = type_to_string(w.type); + error(call, "Mismatched types to 'quaternion', '%s' vs '%s' vs '%s' vs '%s'", tx, ty, tz, tw); + gb_string_free(tw); + gb_string_free(tz); + gb_string_free(ty); + gb_string_free(tx); + return false; + } + + if (!is_type_float(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments have type '%s', expected a floating point", s); + gb_string_free(s); + return false; + } + + if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) { + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + f64 j = exact_value_to_float(z.value).value_float; + f64 k = exact_value_to_float(w.value).value_float; + operand->value = exact_value_quaternion(r, i, j, k); + operand->mode = Addressing_Constant; + } else { + operand->mode = Addressing_Value; + } + + BasicKind kind = core_type(x.type)->Basic.kind; + switch (kind) { + case Basic_f32: operand->type = t_quaternion128; break; + case Basic_f64: operand->type = t_quaternion256; break; + case Basic_UntypedFloat: operand->type = t_untyped_quaternion; break; + default: GB_PANIC("Invalid type"); break; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + break; } case BuiltinProc_real: case BuiltinProc_imag: { // real :: proc(x: type) -> float_type - // proc imag(x: type) -> float_type + // imag :: proc(x: type) -> float_type Operand *x = operand; if (is_type_untyped(x->type)) { @@ -3719,7 +4059,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (is_type_numeric(x->type)) { x->type = t_untyped_complex; } - } else { + } else if (is_type_quaternion(x->type)) { + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } else{ convert_to_typed(c, x, t_complex128); if (x->mode == Addressing_Invalid) { return false; @@ -3727,9 +4072,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } } - if (!is_type_complex(x->type)) { + if (!is_type_complex(x->type) && !is_type_quaternion(x->type)) { gbString s = type_to_string(x->type); - error(call, "Argument has type '%s', expected a complex type", s); + error(call, "Argument has type '%s', expected a complex or quaternion type", s); gb_string_free(s); return false; } @@ -3747,10 +4092,68 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 switch (kind) { case Basic_complex64: x->type = t_f32; break; case Basic_complex128: x->type = t_f64; break; + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; + case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; + default: GB_PANIC("Invalid type"); break; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + break; + } + + case BuiltinProc_jmag: + case BuiltinProc_kmag: { + // jmag :: proc(x: type) -> float_type + // kmag :: proc(x: type) -> float_type + + Operand *x = operand; + if (is_type_untyped(x->type)) { + if (x->mode == Addressing_Constant) { + if (is_type_numeric(x->type)) { + x->type = t_untyped_complex; + } + } else{ + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } + } + + if (!is_type_quaternion(x->type)) { + gbString s = type_to_string(x->type); + error(call, "Argument has type '%s', expected a quaternion type", s); + gb_string_free(s); + return false; + } + + if (x->mode == Addressing_Constant) { + switch (id) { + case BuiltinProc_jmag: x->value = exact_value_jmag(x->value); break; + case BuiltinProc_kmag: x->value = exact_value_kmag(x->value); break; + } + } else { + x->mode = Addressing_Value; + } + + BasicKind kind = core_type(x->type)->Basic.kind; + switch (kind) { + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; default: GB_PANIC("Invalid type"); break; } + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + break; } @@ -3761,12 +4164,24 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (x->mode == Addressing_Constant) { ExactValue v = exact_value_to_complex(x->value); f64 r = v.value_complex.real; - f64 i = v.value_complex.imag; + f64 i = -v.value_complex.imag; x->value = exact_value_complex(r, i); x->mode = Addressing_Constant; } else { x->mode = Addressing_Value; } + } else if (is_type_quaternion(x->type)) { + if (x->mode == Addressing_Constant) { + ExactValue v = exact_value_to_quaternion(x->value); + f64 r = v.value_quaternion.real; + f64 i = -v.value_quaternion.imag; + f64 j = -v.value_quaternion.jmag; + f64 k = -v.value_quaternion.kmag; + x->value = exact_value_quaternion(r, i, j, k); + x->mode = Addressing_Constant; + } else { + x->mode = Addressing_Value; + } } else { gbString s = type_to_string(x->type); error(call, "Expected a complex or quaternion, got '%s'", s); @@ -3959,6 +4374,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Type *bt = base_type(operands[0].type); if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "min_f32"); if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "min_f64"); + + operand->type = operands[0].type; } } break; @@ -4119,6 +4536,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Type *bt = base_type(operands[0].type); if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "max_f32"); if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "max_f64"); + + operand->type = operands[0].type; } } break; @@ -4161,6 +4580,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "abs_f64"); if (are_types_identical(bt, t_complex64)) add_package_dependency(c, "runtime", "abs_complex64"); if (are_types_identical(bt, t_complex128)) add_package_dependency(c, "runtime", "abs_complex128"); + if (are_types_identical(bt, t_quaternion128)) add_package_dependency(c, "runtime", "abs_quaternion128"); + if (are_types_identical(bt, t_quaternion256)) add_package_dependency(c, "runtime", "abs_quaternion256"); } } @@ -4266,6 +4687,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 add_package_dependency(c, "runtime", "min_f64"); add_package_dependency(c, "runtime", "max_f64"); } + + operand->type = ops[0]->type; } } @@ -4439,6 +4862,123 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } break; + + case BuiltinProc_type_base_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->type = base_type(operand->type); + } + operand->mode = Addressing_Type; + break; + case BuiltinProc_type_core_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->type = core_type(operand->type); + } + operand->mode = Addressing_Type; + break; + case BuiltinProc_type_elem_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + switch (bt->kind) { + case Type_Basic: + switch (bt->Basic.kind) { + case Basic_complex64: operand->type = t_f32; break; + case Basic_complex128: operand->type = t_f64; break; + } + break; + case Type_Pointer: operand->type = bt->Pointer.elem; break; + case Type_Opaque: operand->type = bt->Opaque.elem; break; + case Type_Array: operand->type = bt->Array.elem; break; + case Type_Slice: operand->type = bt->Slice.elem; break; + case Type_DynamicArray: operand->type = bt->DynamicArray.elem; break; + } + } + operand->mode = Addressing_Type; + break; + + + case BuiltinProc_type_is_boolean: + case BuiltinProc_type_is_integer: + case BuiltinProc_type_is_rune: + case BuiltinProc_type_is_float: + case BuiltinProc_type_is_complex: + case BuiltinProc_type_is_quaternion: + case BuiltinProc_type_is_string: + case BuiltinProc_type_is_typeid: + case BuiltinProc_type_is_any: + case BuiltinProc_type_is_endian_little: + case BuiltinProc_type_is_endian_big: + case BuiltinProc_type_is_numeric: + case BuiltinProc_type_is_ordered: + case BuiltinProc_type_is_ordered_numeric: + case BuiltinProc_type_is_indexable: + case BuiltinProc_type_is_sliceable: + case BuiltinProc_type_is_simple_compare: + case BuiltinProc_type_is_dereferenceable: + case BuiltinProc_type_is_valid_map_key: + case BuiltinProc_type_is_named: + case BuiltinProc_type_is_pointer: + case BuiltinProc_type_is_opaque: + case BuiltinProc_type_is_array: + case BuiltinProc_type_is_slice: + case BuiltinProc_type_is_dynamic_array: + case BuiltinProc_type_is_map: + case BuiltinProc_type_is_struct: + case BuiltinProc_type_is_union: + case BuiltinProc_type_is_enum: + case BuiltinProc_type_is_proc: + case BuiltinProc_type_is_bit_field: + case BuiltinProc_type_is_bit_field_value: + case BuiltinProc_type_is_bit_set: + case BuiltinProc_type_is_simd_vector: + case BuiltinProc_type_has_nil: + GB_ASSERT(BuiltinProc__type_begin < id && id < BuiltinProc__type_end); + operand->value = exact_value_bool(false); + if (operand->mode != Addressing_Type) { + gbString str = expr_to_string(ce->args[0]); + error(operand->expr, "Expected a type for '%.*s', got '%s'", LIT(builtin_name), str); + gb_string_free(str); + } else { + i32 i = id - (BuiltinProc__type_begin+1); + auto procedure = builtin_type_is_procs[i]; + GB_ASSERT_MSG(procedure != nullptr, "%.*s", LIT(builtin_name)); + operand->value = exact_value_bool(procedure(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + + case BuiltinProc_type_proc_parameter_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Proc.param_count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + case BuiltinProc_type_proc_return_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Proc.result_count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; } return true; @@ -4455,7 +4995,7 @@ isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lhs, isize lhs c->decl = decl; // will be reset by the 'defer' any way for_array(k, decl->deps.entries) { Entity *dep = decl->deps.entries[k].ptr; - add_declaration_dependency(c, dep); // TODO(bill): Should this be here? + add_declaration_dependency(c, dep); // TODO(bill): Should this be here? } } } @@ -4774,6 +5314,11 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } } score += s; + + if (o.mode == Addressing_Type && is_type_typeid(e->type)) { + add_type_info_type(c, o.type); + add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type)); + } } if (variadic) { @@ -4816,6 +5361,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (is_type_any(elem)) { add_type_info_type(c, o.type); } + if (o.mode == Addressing_Type && is_type_typeid(t)) { + add_type_info_type(c, o.type); + add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); + } } } } @@ -5016,6 +5565,11 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { } score += s; } + + if (o->mode == Addressing_Type && is_type_typeid(e->type)) { + add_type_info_type(c, o->type); + add_type_and_value(c->info, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type)); + } } if (data) { @@ -5061,6 +5615,69 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize return lhs; } + +bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *clauses, bool print_err) { + if (clauses != nullptr) { + for_array(i, *clauses) { + Ast *clause = (*clauses)[i]; + Operand o = {}; + check_expr(ctx, &o, clause); + if (o.mode != Addressing_Constant) { + if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation"); + return false; + } else if (o.value.kind != ExactValue_Bool) { + if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation"); + return false; + } else if (!o.value.value_bool) { + if (print_err) { + gbString str = expr_to_string(clause); + error(clause, "'where' clause evaluated to false:\n\t%s", str); + gb_string_free(str); + + if (scope != nullptr) { + isize print_count = 0; + for_array(j, scope->elements.entries) { + Entity *e = scope->elements.entries[j].value; + switch (e->kind) { + case Entity_TypeName: { + if (print_count == 0) error_line("\n\tWith the following definitions:\n"); + + gbString str = type_to_string(e->type); + error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str); + gb_string_free(str); + print_count += 1; + break; + } + case Entity_Constant: { + if (print_count == 0) error_line("\n\tWith the following definitions:\n"); + + gbString str = exact_value_to_string(e->Constant.value); + if (is_type_untyped(e->type)) { + error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str); + } else { + gbString t = type_to_string(e->type); + error_line("\t\t%.*s : %s : %s;\n", LIT(e->token.string), t, str); + gb_string_free(t); + } + gb_string_free(str); + + print_count += 1; + break; + } + } + } + } + + } + return false; + } + } + } + + return true; +} + + CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call) { ast_node(ce, CallExpr, call); @@ -5238,6 +5855,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } + if (proc_arg_count >= 0 && proc_arg_count_all_equal) { lhs_count = proc_arg_count; if (lhs_count > 0) { @@ -5281,9 +5899,8 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type gb_free(heap_allocator(), lhs); } - ValidIndexAndScore *valids = gb_alloc_array(heap_allocator(), ValidIndexAndScore, procs.count); - isize valid_count = 0; - defer (gb_free(heap_allocator(), valids)); + auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count); + defer (array_free(&valids)); gbString expr_name = expr_to_string(operand->expr); defer (gb_string_free(expr_name)); @@ -5298,36 +5915,54 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type ctx.no_polymorphic_errors = true; ctx.allow_polymorphic_types = is_type_polymorphic(pt); + ctx.hide_polymorphic_errors = true; err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data); - if (err == CallArgumentError_None) { - valids[valid_count].index = i; - valids[valid_count].score = data.score; - valid_count++; + if (err != CallArgumentError_None) { + continue; + } + + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + if (!evaluate_where_clauses(&ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) { + continue; + } } + + ValidIndexAndScore item = {}; + item.index = i; + item.score = data.score; + array_add(&valids, item); } } - if (valid_count > 1) { - gb_sort_array(valids, valid_count, valid_index_and_score_cmp); + if (valids.count > 1) { + gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp); i64 best_score = valids[0].score; Entity *best_entity = procs[valids[0].index]; - for (isize i = 1; i < valid_count; i++) { + for (isize i = 1; i < valids.count; i++) { if (best_score > valids[i].score) { - valid_count = i; + valids.count = i; break; } if (best_entity == procs[valids[i].index]) { - valid_count = i; + valids.count = i; break; } - best_score = valids[i].score; } } - if (valid_count == 0) { + if (valids.count == 0) { begin_error_block(); defer (end_error_block()); @@ -5382,7 +6017,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } result_type = t_invalid; - } else if (valid_count > 1) { + } else if (valids.count > 1) { begin_error_block(); defer (end_error_block()); @@ -5397,11 +6032,11 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } error_line(")\n"); - for (isize i = 0; i < valid_count; i++) { + for (isize i = 0; i < valids.count; i++) { Entity *proc = procs[valids[i].index]; TokenPos pos = proc->token.pos; Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc); - gbString pt; + gbString pt = nullptr; defer (gb_string_free(pt)); if (t->Proc.node != nullptr) { pt = expr_to_string(t->Proc.node); @@ -5413,7 +6048,29 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (proc->kind == Entity_Variable) { sep = ":="; } - error_line("\t%.*s %s %s at %.*s(%td:%td)\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column); + error_line("\t%.*s %s %s ", LIT(name), sep, pt); + if (proc->decl_info->proc_lit != nullptr) { + GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit); + auto *pl = &proc->decl_info->proc_lit->ProcLit; + if (pl->where_token.kind != Token_Invalid) { + error_line("\n\t\twhere "); + for_array(j, pl->where_clauses) { + Ast *clause = pl->where_clauses[j]; + if (j != 0) { + error_line("\t\t "); + } + gbString str = expr_to_string(clause); + error_line("%s", str); + gb_string_free(str); + + if (j != pl->where_clauses.count-1) { + error_line(","); + } + } + error_line("\n\t"); + } + } + error_line("at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column); // error_line("\t%.*s %s %s at %.*s(%td:%td) %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, valids[i].score); } result_type = t_invalid; @@ -5492,24 +6149,57 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper defer (array_free(&operands)); bool named_fields = false; + { + // NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves + auto prev_type_path = c->type_path; + c->type_path = new_checker_type_path(); + defer ({ + destroy_checker_type_path(c->type_path); + c->type_path = prev_type_path; + }); + + if (is_call_expr_field_value(ce)) { + named_fields = true; + operands = array_make<Operand>(heap_allocator(), ce->args.count); + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + ast_node(fv, FieldValue, arg); + + if (fv->field->kind == Ast_Ident) { + String name = fv->field->Ident.token.string; + isize index = lookup_polymorphic_record_parameter(original_type, name); + if (index >= 0) { + TypeTuple *params = get_record_polymorphic_params(original_type); + Entity *e = params->variables[i]; + if (e->kind == Entity_Constant) { + check_expr_with_type_hint(c, &operands[i], fv->value, e->type); + } + } - if (is_call_expr_field_value(ce)) { - named_fields = true; - operands = array_make<Operand>(heap_allocator(), ce->args.count); - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - ast_node(fv, FieldValue, arg); - check_expr_or_type(c, &operands[i], fv->value); - } + } + check_expr_or_type(c, &operands[i], fv->value); + } + + bool vari_expand = (ce->ellipsis.pos.line != 0); + if (vari_expand) { + error(ce->ellipsis, "Invalid use of '..' in a polymorphic type call'"); + } - bool vari_expand = (ce->ellipsis.pos.line != 0); - if (vari_expand) { - error(ce->ellipsis, "Invalid use of '..' in a polymorphic type call'"); + } else { + operands = array_make<Operand>(heap_allocator(), 0, 2*ce->args.count); + + Entity **lhs = nullptr; + isize lhs_count = -1; + + TypeTuple *params = get_record_polymorphic_params(original_type); + if (params != nullptr) { + lhs = params->variables.data; + lhs_count = params->variables.count; + } + + check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, false); } - } else { - operands = array_make<Operand>(heap_allocator(), 0, 2*ce->args.count); - check_unpack_arguments(c, nullptr, -1, &operands, ce->args, false, false); } CallArgumentError err = CallArgumentError_None; @@ -5638,12 +6328,16 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper } score += s; } + + // NOTE(bill): Add type info the parameters + add_type_info_type(c, o->type); } { gbAllocator a = c->allocator; - Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands); + bool failure = false; + Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure); if (found_entity) { operand->mode = Addressing_Type; operand->type = found_entity->type; @@ -5692,13 +6386,13 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper -ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { +ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) { ast_node(ce, CallExpr, call); if (ce->proc != nullptr && ce->proc->kind == Ast_BasicDirective) { ast_node(bd, BasicDirective, ce->proc); String name = bd->name; - if (name == "location" || name == "assert" || name == "defined" || name == "load") { + if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "load") { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; operand->expr = ce->proc; @@ -5761,7 +6455,8 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { Ast *s = ident->SelectorExpr.selector; ident = s; } - Type *ot = operand->type; GB_ASSERT(ot->kind == Type_Named); + Type *ot = operand->type; + GB_ASSERT(ot->kind == Type_Named); Entity *e = ot->Named.type_name; add_entity_use(c, ident, e); add_type_and_value(&c->checker->info, call, Addressing_Type, ot, empty_exact_value); @@ -5799,7 +6494,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { if (operand->mode == Addressing_Builtin) { i32 id = operand->builtin_id; - if (!check_builtin_procedure(c, operand, call, id)) { + if (!check_builtin_procedure(c, operand, call, id, type_hint)) { operand->mode = Addressing_Invalid; } operand->expr = call; @@ -5825,17 +6520,6 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { } } - // NOTE(bill): Should this be here or on the `add_entity_use`? - // if (ce->proc != nullptr) { - // Entity *e = entity_of_node(&c->info, ce->proc); - // if (e != nullptr && e->kind == Entity_Procedure) { - // String msg = e->Procedure.deprecated_message; - // if (msg.len > 0) { - // warning(call, "%.*s is deprecated: %.*s", LIT(e->token.string), LIT(msg)); - // } - // } - // } - CallArgumentData data = check_call_arguments(c, operand, proc_type, call); Type *result_type = data.result_type; gb_zero_item(operand); @@ -5975,6 +6659,98 @@ bool ternary_compare_types(Type *x, Type *y) { } +bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValue *inline_for_depth_) { + if (!is_ast_range(node)) { + return false; + } + + ast_node(ie, BinaryExpr, node); + + check_expr(c, x, ie->left); + if (x->mode == Addressing_Invalid) { + return false; + } + check_expr(c, y, ie->right); + if (y->mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, x, y->type); + if (x->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, y, x->type); + if (y->mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, x, default_type(y->type)); + if (x->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, y, default_type(x->type)); + if (y->mode == Addressing_Invalid) { + return false; + } + + 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(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); + gb_string_free(expr_str); + gb_string_free(yt); + gb_string_free(xt); + } + return false; + } + + Type *type = x->type; + if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { + error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); + return false; + } + + if (x->mode == Addressing_Constant && + y->mode == Addressing_Constant) { + ExactValue a = x->value; + ExactValue b = y->value; + + GB_ASSERT(are_types_identical(x->type, y->type)); + + TokenKind op = Token_Lt; + switch (ie->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeHalf: op = Token_Lt; break; + default: error(ie->op, "Invalid range operator"); break; + } + bool ok = compare_exact_values(op, a, b); + if (!ok) { + // TODO(bill): Better error message + error(ie->op, "Invalid interval range"); + return false; + } + + ExactValue inline_for_depth = exact_value_sub(b, a); + if (ie->op.kind == Token_Ellipsis) { + inline_for_depth = exact_value_increment_one(inline_for_depth); + } + + if (inline_for_depth_) *inline_for_depth_ = inline_for_depth; + } else { + error(ie->op, "Interval expressions must be constant"); + return false; + } + + add_type_and_value(&c->checker->info, ie->left, x->mode, x->type, x->value); + add_type_and_value(&c->checker->info, ie->right, y->mode, y->type, y->value); + + return true; +} + + ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Stmt; @@ -6037,32 +6813,26 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(bl, BasicLit, node); - // NOTE(bill, 2018-06-17): Placing this in the parser is slower than - // placing it here for some reason. So don't move it to the parsing - // stage if you _think_ it will be faster, only do it if you _know_ it - // will be faster. Type *t = t_invalid; - switch (bl->token.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; - case Token_Imag: { - String s = bl->token.string; - Rune r = s[s.len-1]; - switch (r) { - case 'i': t = t_untyped_complex; break; + switch (bl->value.kind) { + case ExactValue_String: t = t_untyped_string; break; + case ExactValue_Float: t = t_untyped_float; break; + case ExactValue_Complex: t = t_untyped_complex; break; + case ExactValue_Quaternion: t = t_untyped_quaternion; break; + case ExactValue_Integer: + t = t_untyped_integer; + if (bl->token.kind == Token_Rune) { + t = t_untyped_rune; } - break; - } default: - GB_PANIC("Unknown literal"); + GB_PANIC("Unhandled value type for basic literal"); break; } + o->mode = Addressing_Constant; o->type = t; - o->value = exact_value_from_basic_literal(bl->token); + o->value = bl->value; case_end; case_ast_node(bd, BasicDirective, node); @@ -6107,6 +6877,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type decl = make_decl_info(ctx.allocator, ctx.scope, ctx.decl); decl->proc_lit = node; ctx.decl = decl; + defer (ctx.decl = ctx.decl->parent); if (pl->tags != 0) { error(node, "A procedure literal cannot have tags"); @@ -6412,7 +7183,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type i64 max = 0; - isize index = 0; Type *bet = base_type(elem_type); if (!elem_type_can_be_constant(bet)) { @@ -6423,40 +7193,152 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type break; } - for (; index < cl->elems.count; index++) { - Ast *e = cl->elems[index]; - if (e == nullptr) { - error(node, "Invalid literal element"); - continue; - } + if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { + if (is_type_simd_vector(t)) { + error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); + } else { + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); - if (e->kind == Ast_FieldValue) { - error(e, "'field = value' is only allowed in struct literals"); - continue; - } + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; + + Operand x = {}; + Operand y = {}; + bool ok = check_range(c, fv->field, &x, &y, nullptr); + if (!ok) { + continue; + } + if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { + error(x.expr, "Expected a constant integer as an array field"); + continue; + } + + if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { + error(y.expr, "Expected a constant integer as an array field"); + continue; + } + + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + i64 max_index = hi; + if (op.kind == Token_RangeHalf) { + hi -= 1; + } + + bool new_range = range_cache_add_range(&rc, lo, hi); + if (!new_range) { + error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); + continue; + } + + + if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); + continue; + } + if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); + continue; + } + + if (max < hi) { + max = max_index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } else { + Operand op_index = {}; + check_expr(c, &op_index, fv->field); + + if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { + error(elem, "Expected a constant integer as an array field"); + continue; + } - if (0 <= max_type_count && max_type_count <= index) { - error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + i64 index = exact_value_to_i64(op_index.value); + + if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); + continue; + } + + bool new_index = range_cache_add_index(&rc, index); + if (!new_index) { + error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); + continue; + } + + if (max < index+1) { + max = index+1; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } + } + + cl->max_count = max; } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, e, elem_type); - check_assignment(c, &operand, elem_type, context_name); - is_constant = is_constant && operand.mode == Addressing_Constant; - } - if (max < index) { - max = index; + } else { + isize index = 0; + for (; index < cl->elems.count; index++) { + Ast *e = cl->elems[index]; + if (e == nullptr) { + error(node, "Invalid literal element"); + continue; + } + + if (e->kind == Ast_FieldValue) { + error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + + if (0 <= max_type_count && max_type_count <= index) { + error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, e, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } + + if (max < index) { + max = index; + } } + if (t->kind == Type_Array) { if (is_to_be_determined_array_count) { t->Array.count = max; - } else if (0 < max && max < t->Array.count) { - error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < t->Array.count) { + error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + } } } + if (t->kind == Type_SimdVector) { if (!is_constant) { error(node, "Expected all constant elements for a simd vector"); @@ -6828,7 +7710,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(be, BinaryExpr, node); - check_binary_expr(c, o, node, true); + check_binary_expr(c, o, node, type_hint, true); if (o->mode == Addressing_Invalid) { o->expr = node; return kind; @@ -6992,7 +7874,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (o->mode == Addressing_Constant) { max_count = o->value.value_string.len; } - o->type = t_string; + o->type = type_deref(o->type); } break; @@ -7072,7 +7954,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(ce, CallExpr, node); - return check_call_expr(c, o, node); + return check_call_expr(c, o, node, type_hint); case_end; case_ast_node(de, DerefExpr, node); @@ -7111,6 +7993,9 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case Ast_UnionType: case Ast_EnumType: case Ast_MapType: + case Ast_OpaqueType: + case Ast_BitSetType: + case Ast_BitFieldType: o->mode = Addressing_Type; o->type = check_type(c, node); break; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index e2090688f..d4398664b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -132,6 +132,10 @@ bool check_is_terminating(Ast *node) { } case_end; + case_ast_node(rs, InlineRangeStmt, node); + return false; + case_end; + case_ast_node(rs, RangeStmt, node); return false; case_end; @@ -492,8 +496,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b for_array(i, found->elements.entries) { Entity *f = found->elements.entries[i].value; if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); - uvar->using_expr = expr; + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, expr); Entity *prev = scope_insert(ctx->scope, uvar); if (prev != nullptr) { gbString expr_str = expr_to_string(expr); @@ -587,6 +590,162 @@ void add_constant_switch_case(CheckerContext *ctx, Map<TypeAndToken> *seen, Oper multi_map_insert(seen, key, tap); } +void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { + ast_node(irs, InlineRangeStmt, node); + check_open_scope(ctx, node); + + Type *val0 = nullptr; + Type *val1 = nullptr; + Entity *entities[2] = {}; + isize entity_count = 0; + + Ast *expr = unparen_expr(irs->expr); + + ExactValue inline_for_depth = exact_value_i64(0); + + if (is_ast_range(expr)) { + ast_node(ie, BinaryExpr, expr); + Operand x = {}; + Operand y = {}; + + bool ok = check_range(ctx, expr, &x, &y, &inline_for_depth); + if (!ok) { + goto skip_expr; + } + + val0 = x.type; + val1 = t_int; + } else { + Operand operand = {Addressing_Invalid}; + check_expr_or_type(ctx, &operand, irs->expr); + + if (operand.mode == Addressing_Type) { + if (!is_type_enum(operand.type)) { + gbString t = type_to_string(operand.type); + error(operand.expr, "Cannot iterate over the type '%s'", t); + gb_string_free(t); + goto skip_expr; + } else { + val0 = operand.type; + val1 = t_int; + add_type_info_type(ctx, operand.type); + + Type *bt = base_type(operand.type); + inline_for_depth = exact_value_i64(bt->Enum.fields.count); + goto skip_expr; + } + } else if (operand.mode != Addressing_Invalid) { + Type *t = base_type(operand.type); + switch (t->kind) { + case Type_Basic: + if (is_type_string(t) && t->Basic.kind != Basic_cstring) { + val0 = t_rune; + val1 = t_int; + inline_for_depth = exact_value_i64(operand.value.value_string.len); + } + break; + case Type_Array: + val0 = t->Array.elem; + val1 = t_int; + inline_for_depth = exact_value_i64(t->Array.count); + break; + } + } + + if (val0 == nullptr) { + gbString s = expr_to_string(operand.expr); + gbString t = type_to_string(operand.type); + error(operand.expr, "Cannot iterate over '%s' of type '%s' in an 'inline for' statement", s, t); + gb_string_free(t); + gb_string_free(s); + } else if (operand.mode != Addressing_Constant) { + error(operand.expr, "An 'inline for' expression must be known at compile time"); + } + } + + skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. + + Ast * lhs[2] = {irs->val0, irs->val1}; + Type *rhs[2] = {val0, val1}; + + for (isize i = 0; i < 2; i++) { + if (lhs[i] == nullptr) { + continue; + } + Ast * name = lhs[i]; + Type *type = rhs[i]; + + Entity *entity = nullptr; + if (name->kind == Ast_Ident) { + Token token = name->Ident.token; + String str = token.string; + Entity *found = nullptr; + + if (!is_blank_ident(str)) { + found = scope_lookup_current(ctx->scope, str); + } + if (found == nullptr) { + bool is_immutable = true; + entity = alloc_entity_variable(ctx->scope, token, type, is_immutable, EntityState_Resolved); + entity->flags |= EntityFlag_Value; + add_entity_definition(&ctx->checker->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(name, "A variable declaration must be an identifier"); + } + + if (entity == nullptr) { + entity = alloc_entity_dummy_variable(builtin_pkg->scope, ast_token(name)); + } + + entities[entity_count++] = entity; + + if (type == nullptr) { + entity->type = t_invalid; + entity->flags |= EntityFlag_Used; + } + } + + for (isize i = 0; i < entity_count; i++) { + add_entity(ctx->checker, ctx->scope, entities[i]->identifier, entities[i]); + } + + + // NOTE(bill): Minimize the amount of nesting of an 'inline for' + i64 prev_inline_for_depth = ctx->inline_for_depth; + defer (ctx->inline_for_depth = prev_inline_for_depth); + { + i64 v = exact_value_to_i64(inline_for_depth); + if (v <= 0) { + // Do nothing + } else { + ctx->inline_for_depth = gb_max(ctx->inline_for_depth, 1) * v; + } + + if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) { + if (prev_inline_for_depth > 0) { + error(node, "Nested 'inline for' loop cannot be inlined as it exceeds the maximum inline for depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH); + } else { + error(node, "'inline for' loop cannot be inlined as it exceeds the maximum inline for depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH); + } + error_line("\tUse a normal 'for' loop instead by removing the 'inline' prefix\n"); + ctx->inline_for_depth = MAX_INLINE_FOR_DEPTH; + } + } + + check_stmt(ctx, irs->body, mod_flags); + + + check_close_scope(ctx); +} + void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(ss, SwitchStmt, node); @@ -971,7 +1130,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { GB_PANIC("Unknown type to type switch statement"); } - if (ptr_set_exists(&seen, y.type)) { + if (type_ptr_set_exists(&seen, y.type)) { TokenPos pos = cc->token.pos; gbString expr_str = expr_to_string(y.expr); error(y.expr, @@ -1023,7 +1182,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { for_array(i, variants) { Type *t = variants[i]; - if (!ptr_set_exists(&seen, t)) { + if (!type_ptr_set_exists(&seen, t)) { array_add(&unhandled, t); } } @@ -1132,7 +1291,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { isize rhs_count = rhs_operands.count; for_array(i, rhs_operands) { if (rhs_operands[i].mode == Addressing_Invalid) { - rhs_count--; + // TODO(bill): Should I ignore invalid parameters? + // rhs_count--; } } @@ -1168,7 +1328,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { be->right = as->rhs[0]; check_expr(ctx, &lhs, as->lhs[0]); - check_binary_expr(ctx, &rhs, &binary_expr, true); + check_binary_expr(ctx, &rhs, &binary_expr, nullptr, true); if (rhs.mode == Addressing_Invalid) { return; } @@ -1298,6 +1458,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_close_scope(ctx); case_end; + case_ast_node(rs, RangeStmt, node); u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; @@ -1320,29 +1481,29 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_expr(ctx, &x, ie->left); if (x.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } check_expr(ctx, &y, ie->right); if (y.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &x, y.type); if (x.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &y, x.type); if (y.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &x, default_type(y.type)); if (x.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &y, default_type(x.type)); if (y.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } if (!are_types_identical(x.type, y.type)) { @@ -1356,13 +1517,13 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { gb_string_free(yt); gb_string_free(xt); } - goto skip_expr; + goto skip_expr_range_stmt; } Type *type = x.type; if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); - goto skip_expr; + goto skip_expr_range_stmt; } if (x.mode == Addressing_Constant && @@ -1382,18 +1543,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (!ok) { // TODO(bill): Better error message error(ie->op, "Invalid interval range"); - goto skip_expr; + goto skip_expr_range_stmt; } } - if (x.mode != Addressing_Constant) { - x.value = empty_exact_value; - } - if (y.mode != Addressing_Constant) { - y.value = empty_exact_value; - } - - add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value); add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value); val0 = type; @@ -1407,12 +1560,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { gbString t = type_to_string(operand.type); error(operand.expr, "Cannot iterate over the type '%s'", t); gb_string_free(t); - goto skip_expr; + goto skip_expr_range_stmt; } else { val0 = operand.type; val1 = t_int; add_type_info_type(ctx, operand.type); - goto skip_expr; + goto skip_expr_range_stmt; } } else if (operand.mode != Addressing_Invalid) { bool is_ptr = is_type_pointer(operand.type); @@ -1457,7 +1610,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } } - skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. + skip_expr_range_stmt:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. + Ast * lhs[2] = {rs->val0, rs->val1}; Type *rhs[2] = {val0, val1}; @@ -1507,7 +1661,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } for (isize i = 0; i < entity_count; i++) { - add_entity(ctx->checker, ctx->scope, entities[i]->identifier, entities[i]); + Entity *e = entities[i]; + DeclInfo *d = decl_info_of_entity(e); + GB_ASSERT(d == nullptr); + add_entity(ctx->checker, ctx->scope, e->identifier, e); + d = make_decl_info(ctx->allocator, ctx->scope, ctx->decl); + add_entity_and_decl_info(ctx, e->identifier, e, d); } check_stmt(ctx, rs->body, new_flags); @@ -1515,6 +1674,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_close_scope(ctx); case_end; + case_ast_node(irs, InlineRangeStmt, node); + check_inline_range_stmt(ctx, node, mod_flags); + case_end; + case_ast_node(ss, SwitchStmt, node); check_switch_stmt(ctx, node, mod_flags); case_end; @@ -1814,7 +1977,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { // TODO(bill): Should a 'continue' happen here? } - for (isize entity_index = 0; entity_index < entity_count; entity_index++) { + for (isize entity_index = 0; entity_index < 1; entity_index++) { Entity *e = entities[entity_index]; if (e == nullptr) { continue; @@ -1833,7 +1996,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { for_array(i, scope->elements.entries) { Entity *f = scope->elements.entries[i].value; if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr); uvar->Variable.is_immutable = is_immutable; Entity *prev = scope_insert(ctx->scope, uvar); if (prev != nullptr) { diff --git a/src/check_type.cpp b/src/check_type.cpp index f4e5b49d0..9f8310e44 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -110,9 +110,10 @@ bool does_field_type_allow_using(Type *t) { return false; } -void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<Ast *> const ¶ms, +void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<String> *tags, Array<Ast *> const ¶ms, isize init_field_capacity, Type *struct_type, String context) { *fields = array_make<Entity *>(heap_allocator(), 0, init_field_capacity); + *tags = array_make<String>(heap_allocator(), 0, init_field_capacity); GB_ASSERT(node->kind == Ast_StructType); GB_ASSERT(struct_type->kind == Type_Struct); @@ -171,6 +172,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index); add_entity(ctx->checker, ctx->scope, name, field); array_add(fields, field); + array_add(tags, p->tag.string); field_src_index += 1; } @@ -246,38 +248,51 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) { } -Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array<Operand> ordered_operands) { +Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure) { auto *found_gen_types = map_get(&ctx->checker->info.gen_types, hash_pointer(original_type)); if (found_gen_types != nullptr) { for_array(i, *found_gen_types) { Entity *e = (*found_gen_types)[i]; Type *t = base_type(e->type); TypeTuple *tuple = get_record_polymorphic_params(t); - bool ok = true; GB_ASSERT(param_count == tuple->variables.count); + + bool skip = false; + for (isize j = 0; j < param_count; j++) { Entity *p = tuple->variables[j]; Operand o = ordered_operands[j]; + Entity *oe = entity_of_node(o.expr); + if (p == oe) { + // NOTE(bill): This is the same type, make sure that it will be be same thing and use that + // Saves on a lot of checking too below + continue; + } + if (p->kind == Entity_TypeName) { if (is_type_polymorphic(o.type)) { // NOTE(bill): Do not add polymorphic version to the gen_types - ok = false; + skip = true; + break; } if (!are_types_identical(o.type, p->type)) { - ok = false; + skip = true; + break; } } else if (p->kind == Entity_Constant) { - if (!are_types_identical(o.type, p->type)) { - ok = false; - } if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) { - ok = false; + skip = true; + break; + } + if (!are_types_identical(o.type, p->type)) { + skip = true; + break; } } else { GB_PANIC("Unknown entity kind"); } } - if (ok) { + if (!skip) { return e; } } @@ -439,8 +454,6 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< if (poly_operands != nullptr) { Operand operand = (*poly_operands)[entities.count]; if (is_type_param) { - GB_ASSERT(operand.mode == Addressing_Type || - operand.mode == Addressing_Invalid); if (is_type_polymorphic(base_type(operand.type))) { is_polymorphic = true; can_check_fields = false; @@ -448,6 +461,10 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< e = alloc_entity_type_name(scope, token, operand.type); e->TypeName.is_type_alias = true; } else { + if (is_type_polymorphic(base_type(operand.type))) { + is_polymorphic = true; + can_check_fields = false; + } e = alloc_entity_constant(scope, token, operand.type, operand.value); } } else { @@ -502,9 +519,13 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< struct_type->Struct.polymorphic_params = polymorphic_params; struct_type->Struct.is_poly_specialized = is_poly_specialized; - if (!is_polymorphic) { - check_struct_fields(ctx, node, &struct_type->Struct.fields, st->fields, min_field_count, struct_type, context); + if (st->where_clauses.count > 0 && st->polymorphic_params == nullptr) { + error(st->where_clauses[0], "'where' clauses can only be used on structures with polymorphic parameters"); + } else { + bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &st->where_clauses, true); + } + check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context); } if (st->align != nullptr) { @@ -686,6 +707,13 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op union_type->Union.is_polymorphic = is_polymorphic; union_type->Union.is_poly_specialized = is_poly_specialized; + if (ut->where_clauses.count > 0 && ut->polymorphic_params == nullptr) { + error(ut->where_clauses[0], "'where' clauses can only be used on unions with polymorphic parameters"); + } else { + bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &ut->where_clauses, true); + } + + for_array(i, ut->variants) { Ast *node = ut->variants[i]; Type *t = check_type_expr(ctx, node, nullptr); @@ -1252,20 +1280,21 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand operand) { bool modify_type = !ctx->no_polymorphic_errors; + bool show_error = modify_type && !ctx->hide_polymorphic_errors; if (!is_operand_value(operand)) { - if (modify_type) { + if (show_error) { error(operand.expr, "Cannot determine polymorphic type from parameter"); } return t_invalid; } if (is_polymorphic_type_assignable(ctx, poly_type, operand.type, false, modify_type)) { - if (modify_type) { - set_procedure_abi_types(ctx, poly_type); + if (show_error) { + set_procedure_abi_types(ctx->allocator, poly_type); } return poly_type; } - if (modify_type) { + if (show_error) { gbString pts = type_to_string(poly_type); gbString ots = type_to_string(operand.type); defer (gb_string_free(pts)); @@ -1538,7 +1567,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is } if (is_poly_name) { - if (type != nullptr && type_expr->kind == Ast_TypeidType) { + if (type_expr != nullptr && type_expr->kind == Ast_TypeidType) { is_type_param = true; } else { if (param_value.kind != ParameterValue_Invalid) { @@ -1622,6 +1651,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is if (op.mode == Addressing_Constant) { poly_const = op.value; } else { + error(op.expr, "Expected a constant value for this polymorphic name parameter"); success = false; } } @@ -1825,6 +1855,282 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { return tuple; } +Array<Type *> systemv_distribute_struct_fields(Type *t) { + Type *bt = core_type(t); + + + isize distributed_cap = 1; + if (bt->kind == Type_Struct) { + distributed_cap = bt->Struct.fields.count; + } + auto distributed = array_make<Type *>(heap_allocator(), 0, distributed_cap); + + i64 sz = type_size_of(bt); + switch (bt->kind) { + case Type_Basic: + switch (bt->Basic.kind){ + case Basic_complex64: + array_add(&distributed, t_f32); + array_add(&distributed, t_f32); + break; + case Basic_complex128: + array_add(&distributed, t_f64); + array_add(&distributed, t_f64); + break; + case Basic_quaternion128: + array_add(&distributed, t_f32); + array_add(&distributed, t_f32); + array_add(&distributed, t_f32); + array_add(&distributed, t_f32); + break; + case Basic_quaternion256: + goto DEFAULT; + case Basic_string: + array_add(&distributed, t_u8_ptr); + array_add(&distributed, t_int); + break; + case Basic_any: + GB_ASSERT(type_size_of(t_uintptr) == type_size_of(t_typeid)); + array_add(&distributed, t_rawptr); + array_add(&distributed, t_uintptr); + break; + + case Basic_u128: + case Basic_i128: + if (build_context.ODIN_OS == "windows") { + array_add(&distributed, alloc_type_simd_vector(2, t_u64)); + } else { + array_add(&distributed, bt); + } + break; + + default: + goto DEFAULT; + } + break; + + case Type_Struct: + if (bt->Struct.is_raw_union) { + goto DEFAULT; + } else { + // IMPORTANT TOOD(bill): handle #packed structs correctly + // IMPORTANT TODO(bill): handle #align structs correctly + for_array(field_index, bt->Struct.fields) { + Entity *f = bt->Struct.fields[field_index]; + auto nested = systemv_distribute_struct_fields(f->type); + array_add_elems(&distributed, nested.data, nested.count); + array_free(&nested); + } + } + break; + + case Type_Array: + for (i64 i = 0; i < bt->Array.count; i++) { + array_add(&distributed, bt->Array.elem); + } + break; + + case Type_BitSet: + array_add(&distributed, bit_set_to_int(bt)); + break; + + case Type_Tuple: + GB_PANIC("Invalid struct field type"); + break; + + case Type_Slice: + array_add(&distributed, t_rawptr); + array_add(&distributed, t_int); + break; + + case Type_Union: + case Type_DynamicArray: + case Type_Map: + case Type_BitField: // TODO(bill): Ignore? + // NOTE(bill, 2019-10-10): Odin specific, don't worry about C calling convention yet + goto DEFAULT; + + case Type_Pointer: + case Type_Proc: + case Type_SimdVector: // TODO(bill): Is this correct logic? + default: + DEFAULT:; + if (sz > 0) { + array_add(&distributed, bt); + } + break; + } + + return distributed; +} + +Type *struct_type_from_systemv_distribute_struct_fields(Type *abi_type) { + GB_ASSERT(is_type_tuple(abi_type)); + Type *final_type = alloc_type_struct(); + final_type->Struct.fields = abi_type->Tuple.variables; + return final_type; +} + + +Type *handle_single_distributed_type_parameter(Array<Type *> const &types, bool packed, isize *offset) { + GB_ASSERT(types.count > 0); + + if (types.count == 1) { + if (offset) *offset = 1; + + i64 sz = type_size_of(types[0]); + + if (is_type_float(types[0])) { + return types[0]; + } + switch (sz) { + case 0: + GB_PANIC("Zero sized type found!"); + case 1: return t_u8; + case 2: return t_u16; + case 4: return t_u32; + case 8: return t_u64; + default: + return types[0]; + } + } else if (types.count >= 2) { + if (types[0] == t_f32 && types[1] == t_f32) { + if (offset) *offset = 2; + return alloc_type_simd_vector(2, t_f32); + } else if (type_size_of(types[0]) == 8) { + if (offset) *offset = 1; + return types[0]; + } + + i64 total_size = 0; + isize i = 0; + if (packed) { + for (; i < types.count && total_size < 8; i += 1) { + Type *t = types[i]; + i64 s = type_size_of(t); + total_size += s; + } + } else { + for (; i < types.count && total_size < 8; i += 1) { + Type *t = types[i]; + i64 s = gb_max(type_size_of(t), 0); + i64 a = gb_max(type_align_of(t), 1); + isize ts = align_formula(total_size, a); + if (ts >= 8) { + break; + } + total_size = ts + s; + } + } + if (offset) *offset = i; + switch (total_size) { + case 1: return t_u8; + case 2: return t_u16; + case 4: return t_u32; + case 8: return t_u64; + } + return t_u64; + } + + return nullptr; +} + +Type *handle_struct_system_v_amd64_abi_type(Type *t) { + if (type_size_of(t) > 16) { + return alloc_type_pointer(t); + } + Type *original_type = t; + Type *bt = core_type(t); + t = base_type(t); + i64 size = type_size_of(bt); + + switch (t->kind) { + case Type_Slice: + case Type_Struct: + break; + + case Type_Basic: + switch (bt->Basic.kind) { + case Basic_string: + case Basic_any: + case Basic_complex64: + case Basic_complex128: + case Basic_quaternion128: + break; + default: + return original_type; + } + break; + + default: + return original_type; + } + + bool is_packed = false; + if (is_type_struct(bt)) { + is_packed = bt->Struct.is_packed; + } + + if (is_type_raw_union(bt)) { + // TODO(bill): Handle raw union correctly for + return t; + } else { + auto field_types = systemv_distribute_struct_fields(bt); + defer (array_free(&field_types)); + + GB_ASSERT(field_types.count <= 16); + + Type *final_type = nullptr; + + if (field_types.count == 0) { + final_type = t; + } else if (field_types.count == 1) { + final_type = field_types[0]; + } else { + if (size <= 8) { + isize offset = 0; + final_type = handle_single_distributed_type_parameter(field_types, is_packed, &offset); + } else { + isize offset = 0; + isize next_offset = 0; + Type *two_types[2] = {}; + + two_types[0] = handle_single_distributed_type_parameter(field_types, is_packed, &offset); + auto remaining = array_slice(field_types, offset, field_types.count); + two_types[1] = handle_single_distributed_type_parameter(remaining, is_packed, &next_offset); + GB_ASSERT(offset + next_offset == field_types.count); + + auto variables = array_make<Entity *>(heap_allocator(), 2); + variables[0] = alloc_entity_param(nullptr, empty_token, two_types[0], false, false); + variables[1] = alloc_entity_param(nullptr, empty_token, two_types[1], false, false); + final_type = alloc_type_tuple(); + final_type->Tuple.variables = variables; + if (t->kind == Type_Struct) { + // NOTE(bill): Make this packed + final_type->Tuple.is_packed = t->Struct.is_packed; + } + } + } + + + GB_ASSERT(final_type != nullptr); + i64 ftsz = type_size_of(final_type); + i64 otsz = type_size_of(original_type); + if (ftsz != otsz) { + // TODO(bill): Handle this case which will be caused by #packed most likely + switch (otsz) { + case 1: + case 2: + case 4: + case 8: + GB_PANIC("Incorrectly handled case for handle_struct_system_v_amd64_abi_type, %s %lld vs %s %lld", type_to_string(final_type), ftsz, type_to_string(original_type), otsz); + } + } + + return final_type; + } +} + Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc) { Type *new_type = original_type; @@ -1889,6 +2195,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall { i64 align = type_align_of(original_type); i64 size = type_size_of(original_type); + switch (8*size) { case 8: new_type = t_u8; break; case 16: new_type = t_u16; break; @@ -1903,7 +2210,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall } } } else if (build_context.ODIN_OS == "linux" || - build_context.ODIN_OS == "osx") { + build_context.ODIN_OS == "darwin") { Type *bt = core_type(original_type); switch (bt->kind) { // Okay to pass by value (usually) @@ -1920,18 +2227,17 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall case Type_Pointer: break; case Type_Proc: break; // NOTE(bill): Just a pointer - // Odin specific - case Type_Slice: - case Type_Array: - case Type_DynamicArray: - case Type_Map: - case Type_Union: - // Could be in C too - case Type_Struct: { - i64 align = type_align_of(original_type); - i64 size = type_size_of(original_type); - if (8*size > 16) { + default: { + i64 size = type_size_of(original_type); + if (size > 16) { new_type = alloc_type_pointer(original_type); + } else if (build_context.ODIN_ARCH == "amd64") { + // NOTE(bill): System V AMD64 ABI + new_type = handle_struct_system_v_amd64_abi_type(bt); + if (are_types_identical(core_type(original_type), new_type)) { + new_type = original_type; + } + return new_type; } break; @@ -2004,8 +2310,10 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal break; } } - } else if (build_context.ODIN_OS == "linux") { + } else if (build_context.ODIN_OS == "linux" || build_context.ODIN_OS == "darwin") { + if (build_context.ODIN_ARCH == "amd64") { + } } else { // IMPORTANT TODO(bill): figure out the ABI settings for Linux, OSX etc. for // their architectures @@ -2071,25 +2379,39 @@ bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type return false; } -void set_procedure_abi_types(CheckerContext *c, Type *type) { +void set_procedure_abi_types(gbAllocator allocator, Type *type) { type = base_type(type); if (type->kind != Type_Proc) { return; } - type->Proc.abi_compat_params = array_make<Type *>(c->allocator, cast(isize)type->Proc.param_count); + if (type->Proc.abi_types_set) { + return; + } + + type->Proc.abi_compat_params = array_make<Type *>(allocator, cast(isize)type->Proc.param_count); for (i32 i = 0; i < type->Proc.param_count; i++) { Entity *e = type->Proc.params->Tuple.variables[i]; if (e->kind == Entity_Variable) { Type *original_type = e->type; - Type *new_type = type_to_abi_compat_param_type(c->allocator, original_type, type->Proc.calling_convention); + Type *new_type = type_to_abi_compat_param_type(allocator, original_type, type->Proc.calling_convention); type->Proc.abi_compat_params[i] = new_type; + switch (type->Proc.calling_convention) { + case ProcCC_Odin: + case ProcCC_Contextless: + if (is_type_pointer(new_type) & !is_type_pointer(e->type)) { + e->flags |= EntityFlag_ImplicitReference; + } + break; + } } } // NOTE(bill): The types are the same - type->Proc.abi_compat_result_type = type_to_abi_compat_result_type(c->allocator, type->Proc.results, type->Proc.calling_convention); - type->Proc.return_by_pointer = abi_compat_return_by_pointer(c->allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type); + type->Proc.abi_compat_result_type = type_to_abi_compat_result_type(allocator, type->Proc.results, type->Proc.calling_convention); + type->Proc.return_by_pointer = abi_compat_return_by_pointer(allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type); + + type->Proc.abi_types_set = true; } // NOTE(bill): 'operands' is for generating non generic procedure type @@ -2187,8 +2509,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, } type->Proc.is_polymorphic = is_polymorphic; - set_procedure_abi_types(c, type); - return success; } @@ -2537,7 +2857,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t generic_type = o.type; } if (count < 0) { - error(at->count, "... can only be used in conjuction with compound literals"); + error(at->count, "? can only be used in conjuction with compound literals"); count = 0; } Type *elem = check_type_expr(ctx, at->elem, nullptr); diff --git a/src/checker.cpp b/src/checker.cpp index 9abd8c499..e7b347f1a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -85,9 +85,13 @@ int entity_graph_node_cmp(EntityGraphNode **data, isize i, isize j) { EntityGraphNode *y = data[j]; isize a = x->entity->order_in_src; isize b = y->entity->order_in_src; - if (x->dep_count < y->dep_count) return -1; - if (x->dep_count > y->dep_count) return +1; - return a < b ? -1 : b > a; + if (x->dep_count < y->dep_count) { + return -1; + } + if (x->dep_count == y->dep_count) { + return a < b ? -1 : b > a; + } + return +1; } void entity_graph_node_swap(EntityGraphNode **data, isize i, isize j) { @@ -400,6 +404,15 @@ Entity *scope_insert_with_name(Scope *s, String name, Entity *entity) { if (found) { return *found; } + if (s->parent != nullptr && (s->parent->flags & ScopeFlag_Proc) != 0) { + Entity **found = map_get(&s->parent->elements, key); + if (found) { + if ((*found)->flags & EntityFlag_Result) { + return *found; + } + } + } + map_set(&s->elements, key, entity); if (entity->scope == nullptr) { entity->scope = s; @@ -1044,21 +1057,37 @@ bool redeclaration_error(String name, Entity *prev, Entity *found) { // NOTE(bill): Error should have been handled already return false; } - error(prev->token, - "Redeclaration of '%.*s' in this scope through 'using'\n" - "\tat %.*s(%td:%td)", - LIT(name), - LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); + if (found->flags & EntityFlag_Result) { + error(prev->token, + "Direct shadowing of the named return value '%.*s' in this scope through 'using'\n" + "\tat %.*s(%td:%td)", + LIT(name), + LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); + } else { + error(prev->token, + "Redeclaration of '%.*s' in this scope through 'using'\n" + "\tat %.*s(%td:%td)", + LIT(name), + LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); + } } else { if (pos == prev->token.pos) { // NOTE(bill): Error should have been handled already return false; } - error(prev->token, - "Redeclaration of '%.*s' in this scope\n" - "\tat %.*s(%td:%td)", - LIT(name), - LIT(pos.file), pos.line, pos.column); + if (found->flags & EntityFlag_Result) { + error(prev->token, + "Direct shadowing of the named return value '%.*s' in this scope\n" + "\tat %.*s(%td:%td)", + LIT(name), + LIT(pos.file), pos.line, pos.column); + } else { + error(prev->token, + "Redeclaration of '%.*s' in this scope\n" + "\tat %.*s(%td:%td)", + LIT(name), + LIT(pos.file), pos.line, pos.column); + } } return false; } @@ -1139,6 +1168,7 @@ void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, Dec add_entity_definition(&c->checker->info, identifier, e); GB_ASSERT(e->decl_info == nullptr); e->decl_info = d; + d->entity = e; array_add(&c->checker->info.entities, e); e->order_in_src = c->checker->info.entities.count; e->pkg = c->pkg; @@ -1415,6 +1445,14 @@ void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, t_type_info_float); add_min_dep_type_info(c, t_f64); break; + case Basic_quaternion128: + add_min_dep_type_info(c, t_type_info_float); + add_min_dep_type_info(c, t_f32); + break; + case Basic_quaternion256: + add_min_dep_type_info(c, t_type_info_float); + add_min_dep_type_info(c, t_f64); + break; } break; @@ -1577,6 +1615,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("args__"), str_lit("type_table"), + str_lit("__type_info_of"), str_lit("global_scratch_allocator"), str_lit("Type_Info"), @@ -1585,9 +1624,17 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("quo_complex64"), str_lit("quo_complex128"), + str_lit("mul_quaternion128"), + str_lit("mul_quaternion256"), + str_lit("quo_quaternion128"), + str_lit("quo_quaternion256"), + str_lit("cstring_to_string"), str_lit("umodti3"), str_lit("udivti3"), + + str_lit("memory_compare"), + str_lit("memory_compare_zero"), }; for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) { add_dependency_to_set(c, scope_lookup(c->info.runtime_package->scope, required_runtime_entities[i])); @@ -1656,9 +1703,10 @@ bool is_entity_a_dependency(Entity *e) { if (e == nullptr) return false; switch (e->kind) { case Entity_Procedure: - case Entity_Variable: - case Entity_Constant: return true; + case Entity_Constant: + case Entity_Variable: + return e->pkg != nullptr; } return false; } @@ -1685,18 +1733,17 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) { EntityGraphNode *n = M.entries[i].value; DeclInfo *decl = decl_info_of_entity(e); - if (decl != nullptr) { - for_array(j, decl->deps.entries) { - auto entry = decl->deps.entries[j]; - Entity *dep = entry.ptr; - if (dep && is_entity_a_dependency(dep)) { - EntityGraphNode **m_ = map_get(&M, hash_pointer(dep)); - if (m_ != nullptr) { - EntityGraphNode *m = *m_; - entity_graph_node_set_add(&n->succ, m); - entity_graph_node_set_add(&m->pred, n); - } - } + GB_ASSERT(decl != nullptr); + + for_array(j, decl->deps.entries) { + Entity *dep = decl->deps.entries[j].ptr; + GB_ASSERT(dep != nullptr); + if (is_entity_a_dependency(dep)) { + EntityGraphNode **m_ = map_get(&M, hash_pointer(dep)); + GB_ASSERT(m_ != nullptr); + EntityGraphNode *m = *m_; + entity_graph_node_set_add(&n->succ, m); + entity_graph_node_set_add(&m->pred, n); } } } @@ -1741,6 +1788,7 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) { EntityGraphNode *n = G[i]; n->index = i; n->dep_count = n->succ.entries.count; + GB_ASSERT(n->dep_count >= 0); } @@ -1863,6 +1911,7 @@ void init_core_type_info(Checker *c) { t_type_info_integer = find_core_type(c, str_lit("Type_Info_Integer")); t_type_info_rune = find_core_type(c, str_lit("Type_Info_Rune")); t_type_info_float = find_core_type(c, str_lit("Type_Info_Float")); + t_type_info_quaternion = find_core_type(c, str_lit("Type_Info_Quaternion")); t_type_info_complex = find_core_type(c, str_lit("Type_Info_Complex")); t_type_info_string = find_core_type(c, str_lit("Type_Info_String")); t_type_info_boolean = find_core_type(c, str_lit("Type_Info_Boolean")); @@ -1887,6 +1936,7 @@ void init_core_type_info(Checker *c) { t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); t_type_info_rune_ptr = alloc_type_pointer(t_type_info_rune); t_type_info_float_ptr = alloc_type_pointer(t_type_info_float); + t_type_info_quaternion_ptr = alloc_type_pointer(t_type_info_quaternion); t_type_info_complex_ptr = alloc_type_pointer(t_type_info_complex); t_type_info_string_ptr = alloc_type_pointer(t_type_info_string); t_type_info_boolean_ptr = alloc_type_pointer(t_type_info_boolean); @@ -2138,6 +2188,12 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "require_results") { + if (value != nullptr) { + error(elem, "Expected no value for '%.*s'", LIT(name)); + } + ac->require_results = true; + return true; } return false; } @@ -2528,6 +2584,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { Ast *init_expr = value; DeclInfo *d = make_decl_info(heap_allocator(), c->scope, c->decl); + d->entity = e; d->type_expr = vd->type; d->init_expr = init_expr; d->attributes = vd->attributes; @@ -2557,14 +2614,14 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { Entity *e = nullptr; d->attributes = vd->attributes; + d->type_expr = vd->type; + d->init_expr = init; if (is_ast_type(init)) { e = alloc_entity_type_name(d->scope, token, nullptr); - if (vd->type != nullptr) { - error(name, "A type declaration cannot have an type parameter"); - } - d->type_expr = init; - d->init_expr = init; + // if (vd->type != nullptr) { + // error(name, "A type declaration cannot have an type parameter"); + // } } else if (init->kind == Ast_ProcLit) { if (c->scope->flags&ScopeFlag_Type) { error(name, "Procedure declarations are not allowed within a struct"); @@ -2591,19 +2648,15 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { pl->type->ProcType.calling_convention = cc; } d->proc_lit = init; - d->type_expr = vd->type; + d->init_expr = init; } else if (init->kind == Ast_ProcGroup) { ast_node(pg, ProcGroup, init); e = alloc_entity_proc_group(d->scope, token, nullptr); if (fl != nullptr) { error(name, "Procedure groups are not allowed within a foreign block"); } - d->init_expr = init; - d->type_expr = vd->type; } else { e = alloc_entity_constant(d->scope, token, nullptr, empty_exact_value); - d->type_expr = vd->type; - d->init_expr = init; } e->identifier = name; @@ -3101,7 +3154,7 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { Entity *e = scope->elements.entries[elem_index].value; if (e->scope == parent_scope) continue; - if (is_entity_exported(e)) { + if (is_entity_exported(e, true)) { Entity *found = scope_lookup_current(parent_scope, name); if (found != nullptr) { // NOTE(bill): @@ -3315,8 +3368,9 @@ bool collect_file_decls(CheckerContext *ctx, Array<Ast *> const &decls) { if (es->expr->kind == Ast_CallExpr) { ast_node(ce, CallExpr, es->expr); if (ce->proc->kind == Ast_BasicDirective) { - Operand o = {}; - check_expr(ctx, &o, es->expr); + if (ctx->collect_delayed_decls) { + array_add(&ctx->scope->delayed_directives, es->expr); + } } } case_end; @@ -3473,12 +3527,18 @@ void check_import_entities(Checker *c) { for_array(i, pkg->files) { AstFile *f = pkg->files[i]; CheckerContext ctx = c->init_ctx; - add_curr_ast_file(&ctx, f); + for_array(j, f->scope->delayed_imports) { Ast *decl = f->scope->delayed_imports[j]; check_add_import_decl(&ctx, decl); } + } + for_array(i, pkg->files) { + AstFile *f = pkg->files[i]; + CheckerContext ctx = c->init_ctx; + add_curr_ast_file(&ctx, f); + for_array(j, f->scope->delayed_directives) { Ast *expr = f->scope->delayed_directives[j]; Operand o = {}; @@ -3542,7 +3602,6 @@ void calculate_global_init_order(Checker *c) { #define TIME_SECTION(str) #endif - CheckerInfo *info = &c->info; TIME_SECTION("generate entity dependency graph"); @@ -3584,21 +3643,26 @@ void calculate_global_init_order(Checker *c) { for_array(i, n->pred.entries) { EntityGraphNode *p = n->pred.entries[i].ptr; - p->dep_count -= gb_max(p->dep_count-1, 0); + p->dep_count -= 1; + p->dep_count = gb_max(p->dep_count, 0); priority_queue_fix(&pq, p->index); } - if (e == nullptr || e->kind != Entity_Variable) { + DeclInfo *d = decl_info_of_entity(e); + if (e->kind != Entity_Variable) { continue; } - DeclInfo *d = decl_info_of_entity(e); - + // IMPORTANT NOTE(bill, 2019-08-29): Just add it regardless of the ordering + // because it does not need any initialization other than zero + // if (!decl_info_has_init(d)) { + // continue; + // } if (ptr_set_exists(&emitted, d)) { continue; } ptr_set_add(&emitted, d); - d->entity = e; + array_add(&info->variable_init_order, d); } @@ -3637,6 +3701,14 @@ void check_proc_info(Checker *c, ProcInfo pi) { return; } + if (pt->is_polymorphic && pt->is_poly_specialized) { + Entity *e = pi.decl->entity; + if ((e->flags & EntityFlag_Used) == 0) { + // NOTE(bill, 2019-08-31): It was never used, don't check + return; + } + } + bool bounds_check = (pi.tags & ProcTag_bounds_check) != 0; bool no_bounds_check = (pi.tags & ProcTag_no_bounds_check) != 0; diff --git a/src/checker.hpp b/src/checker.hpp index 66b68c35c..c33514511 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -59,218 +59,8 @@ struct BuiltinProc { BuiltinProcPkg pkg; }; -enum BuiltinProcId { - BuiltinProc_Invalid, - - BuiltinProc_len, - BuiltinProc_cap, - - BuiltinProc_size_of, - BuiltinProc_align_of, - BuiltinProc_offset_of, - BuiltinProc_type_of, - BuiltinProc_type_info_of, - BuiltinProc_typeid_of, - - BuiltinProc_swizzle, - - BuiltinProc_complex, - BuiltinProc_real, - BuiltinProc_imag, - BuiltinProc_conj, - - BuiltinProc_expand_to_tuple, - - BuiltinProc_min, - BuiltinProc_max, - BuiltinProc_abs, - BuiltinProc_clamp, - - BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures - - // "Intrinsics" - BuiltinProc_vector, - - BuiltinProc_atomic_fence, - BuiltinProc_atomic_fence_acq, - BuiltinProc_atomic_fence_rel, - BuiltinProc_atomic_fence_acqrel, - - BuiltinProc_atomic_store, - BuiltinProc_atomic_store_rel, - BuiltinProc_atomic_store_relaxed, - BuiltinProc_atomic_store_unordered, - - BuiltinProc_atomic_load, - BuiltinProc_atomic_load_acq, - BuiltinProc_atomic_load_relaxed, - BuiltinProc_atomic_load_unordered, - - BuiltinProc_atomic_add, - BuiltinProc_atomic_add_acq, - BuiltinProc_atomic_add_rel, - BuiltinProc_atomic_add_acqrel, - BuiltinProc_atomic_add_relaxed, - BuiltinProc_atomic_sub, - BuiltinProc_atomic_sub_acq, - BuiltinProc_atomic_sub_rel, - BuiltinProc_atomic_sub_acqrel, - BuiltinProc_atomic_sub_relaxed, - BuiltinProc_atomic_and, - BuiltinProc_atomic_and_acq, - BuiltinProc_atomic_and_rel, - BuiltinProc_atomic_and_acqrel, - BuiltinProc_atomic_and_relaxed, - BuiltinProc_atomic_nand, - BuiltinProc_atomic_nand_acq, - BuiltinProc_atomic_nand_rel, - BuiltinProc_atomic_nand_acqrel, - BuiltinProc_atomic_nand_relaxed, - BuiltinProc_atomic_or, - BuiltinProc_atomic_or_acq, - BuiltinProc_atomic_or_rel, - BuiltinProc_atomic_or_acqrel, - BuiltinProc_atomic_or_relaxed, - BuiltinProc_atomic_xor, - BuiltinProc_atomic_xor_acq, - BuiltinProc_atomic_xor_rel, - BuiltinProc_atomic_xor_acqrel, - BuiltinProc_atomic_xor_relaxed, - - BuiltinProc_atomic_xchg, - BuiltinProc_atomic_xchg_acq, - BuiltinProc_atomic_xchg_rel, - BuiltinProc_atomic_xchg_acqrel, - BuiltinProc_atomic_xchg_relaxed, - - BuiltinProc_atomic_cxchg, - BuiltinProc_atomic_cxchg_acq, - BuiltinProc_atomic_cxchg_rel, - BuiltinProc_atomic_cxchg_acqrel, - BuiltinProc_atomic_cxchg_relaxed, - BuiltinProc_atomic_cxchg_failrelaxed, - BuiltinProc_atomic_cxchg_failacq, - BuiltinProc_atomic_cxchg_acq_failrelaxed, - BuiltinProc_atomic_cxchg_acqrel_failrelaxed, - - BuiltinProc_atomic_cxchgweak, - BuiltinProc_atomic_cxchgweak_acq, - BuiltinProc_atomic_cxchgweak_rel, - BuiltinProc_atomic_cxchgweak_acqrel, - BuiltinProc_atomic_cxchgweak_relaxed, - BuiltinProc_atomic_cxchgweak_failrelaxed, - BuiltinProc_atomic_cxchgweak_failacq, - BuiltinProc_atomic_cxchgweak_acq_failrelaxed, - BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed, - - BuiltinProc_COUNT, -}; -gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { - {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_builtin}, - - {STR_LIT("len"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("cap"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("size_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("align_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("offset_of"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("type_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("type_info_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("typeid_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("swizzle"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("complex"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("real"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("imag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("abs"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("clamp"), 3, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE - - - // "Intrinsics" - {STR_LIT("vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type - - - {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, -}; + +#include "checker_builtin_procs.hpp" // Operand is used as an intermediate value whilst checking @@ -307,6 +97,7 @@ struct DeferredProcedure { struct AttributeContext { bool is_export; bool is_static; + bool require_results; String link_name; String link_prefix; isize init_expr_list_count; @@ -431,6 +222,7 @@ struct ForeignContext { typedef Array<Entity *> CheckerTypePath; typedef Array<Type *> CheckerPolyPath; + // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { Map<ExprInfo> untyped; // Key: Ast * | Expression -> ExprInfo @@ -486,10 +278,14 @@ struct CheckerContext { CheckerPolyPath *poly_path; isize poly_level; // TODO(bill): Actually handle correctly +#define MAX_INLINE_FOR_DEPTH 1024ll + i64 inline_for_depth; + bool in_enum_type; bool collect_delayed_decls; bool allow_polymorphic_types; bool no_polymorphic_errors; + bool hide_polymorphic_errors; bool in_polymorphic_specialization; Scope * polymorphic_scope; }; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp new file mode 100644 index 000000000..50d27d715 --- /dev/null +++ b/src/checker_builtin_procs.hpp @@ -0,0 +1,320 @@ +// checker_builtin_procs.hpp + +enum BuiltinProcId { + BuiltinProc_Invalid, + + BuiltinProc_len, + BuiltinProc_cap, + + BuiltinProc_size_of, + BuiltinProc_align_of, + BuiltinProc_offset_of, + BuiltinProc_type_of, + BuiltinProc_type_info_of, + BuiltinProc_typeid_of, + + BuiltinProc_swizzle, + + BuiltinProc_complex, + BuiltinProc_quaternion, + BuiltinProc_real, + BuiltinProc_imag, + BuiltinProc_jmag, + BuiltinProc_kmag, + BuiltinProc_conj, + + BuiltinProc_expand_to_tuple, + + BuiltinProc_min, + BuiltinProc_max, + BuiltinProc_abs, + BuiltinProc_clamp, + + BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures + + // "Intrinsics" + BuiltinProc_vector, + + BuiltinProc_atomic_fence, + BuiltinProc_atomic_fence_acq, + BuiltinProc_atomic_fence_rel, + BuiltinProc_atomic_fence_acqrel, + + BuiltinProc_atomic_store, + BuiltinProc_atomic_store_rel, + BuiltinProc_atomic_store_relaxed, + BuiltinProc_atomic_store_unordered, + + BuiltinProc_atomic_load, + BuiltinProc_atomic_load_acq, + BuiltinProc_atomic_load_relaxed, + BuiltinProc_atomic_load_unordered, + + BuiltinProc_atomic_add, + BuiltinProc_atomic_add_acq, + BuiltinProc_atomic_add_rel, + BuiltinProc_atomic_add_acqrel, + BuiltinProc_atomic_add_relaxed, + BuiltinProc_atomic_sub, + BuiltinProc_atomic_sub_acq, + BuiltinProc_atomic_sub_rel, + BuiltinProc_atomic_sub_acqrel, + BuiltinProc_atomic_sub_relaxed, + BuiltinProc_atomic_and, + BuiltinProc_atomic_and_acq, + BuiltinProc_atomic_and_rel, + BuiltinProc_atomic_and_acqrel, + BuiltinProc_atomic_and_relaxed, + BuiltinProc_atomic_nand, + BuiltinProc_atomic_nand_acq, + BuiltinProc_atomic_nand_rel, + BuiltinProc_atomic_nand_acqrel, + BuiltinProc_atomic_nand_relaxed, + BuiltinProc_atomic_or, + BuiltinProc_atomic_or_acq, + BuiltinProc_atomic_or_rel, + BuiltinProc_atomic_or_acqrel, + BuiltinProc_atomic_or_relaxed, + BuiltinProc_atomic_xor, + BuiltinProc_atomic_xor_acq, + BuiltinProc_atomic_xor_rel, + BuiltinProc_atomic_xor_acqrel, + BuiltinProc_atomic_xor_relaxed, + + BuiltinProc_atomic_xchg, + BuiltinProc_atomic_xchg_acq, + BuiltinProc_atomic_xchg_rel, + BuiltinProc_atomic_xchg_acqrel, + BuiltinProc_atomic_xchg_relaxed, + + BuiltinProc_atomic_cxchg, + BuiltinProc_atomic_cxchg_acq, + BuiltinProc_atomic_cxchg_rel, + BuiltinProc_atomic_cxchg_acqrel, + BuiltinProc_atomic_cxchg_relaxed, + BuiltinProc_atomic_cxchg_failrelaxed, + BuiltinProc_atomic_cxchg_failacq, + BuiltinProc_atomic_cxchg_acq_failrelaxed, + BuiltinProc_atomic_cxchg_acqrel_failrelaxed, + + BuiltinProc_atomic_cxchgweak, + BuiltinProc_atomic_cxchgweak_acq, + BuiltinProc_atomic_cxchgweak_rel, + BuiltinProc_atomic_cxchgweak_acqrel, + BuiltinProc_atomic_cxchgweak_relaxed, + BuiltinProc_atomic_cxchgweak_failrelaxed, + BuiltinProc_atomic_cxchgweak_failacq, + BuiltinProc_atomic_cxchgweak_acq_failrelaxed, + BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed, + + + // Constant type tests +BuiltinProc__type_begin, + + BuiltinProc_type_base_type, + BuiltinProc_type_core_type, + BuiltinProc_type_elem_type, + + BuiltinProc_type_is_boolean, + BuiltinProc_type_is_integer, + BuiltinProc_type_is_rune, + BuiltinProc_type_is_float, + BuiltinProc_type_is_complex, + BuiltinProc_type_is_quaternion, + BuiltinProc_type_is_string, + BuiltinProc_type_is_typeid, + BuiltinProc_type_is_any, + + BuiltinProc_type_is_endian_little, + BuiltinProc_type_is_endian_big, + BuiltinProc_type_is_numeric, + BuiltinProc_type_is_ordered, + BuiltinProc_type_is_ordered_numeric, + BuiltinProc_type_is_indexable, + BuiltinProc_type_is_sliceable, + BuiltinProc_type_is_simple_compare, // easily compared using memcmp + BuiltinProc_type_is_dereferenceable, + BuiltinProc_type_is_valid_map_key, + + BuiltinProc_type_is_named, + BuiltinProc_type_is_pointer, + BuiltinProc_type_is_opaque, + BuiltinProc_type_is_array, + BuiltinProc_type_is_slice, + BuiltinProc_type_is_dynamic_array, + BuiltinProc_type_is_map, + BuiltinProc_type_is_struct, + BuiltinProc_type_is_union, + BuiltinProc_type_is_enum, + BuiltinProc_type_is_proc, + BuiltinProc_type_is_bit_field, + BuiltinProc_type_is_bit_field_value, + BuiltinProc_type_is_bit_set, + BuiltinProc_type_is_simd_vector, + + BuiltinProc_type_has_nil, + + BuiltinProc_type_proc_parameter_count, + BuiltinProc_type_proc_return_count, + +BuiltinProc__type_end, + + BuiltinProc_COUNT, +}; +gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_builtin}, + + {STR_LIT("len"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("cap"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("size_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("align_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("offset_of"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("type_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("type_info_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("typeid_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("swizzle"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("complex"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("quaternion"), 4, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("real"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("imag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("jmag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("kmag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("abs"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("clamp"), 3, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE + + + // "Intrinsics" + {STR_LIT("vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type + + {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_base_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_elem_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_integer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_rune"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_float"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_complex"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_quaternion"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_string"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_typeid"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_is_endian_little"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_endian_big"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_numeric"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_ordered_numeric"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_indexable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_sliceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_dereferenceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_valid_map_key"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_is_named"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_opaque"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_slice"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_dynamic_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_map"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_struct"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_field_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_has_nil"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_proc_return_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, +}; diff --git a/src/common.cpp b/src/common.cpp index 8085e895c..b034ad720 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -10,13 +10,11 @@ #define GB_IMPLEMENTATION #include "gb/gb.h" - #include <wchar.h> #include <stdio.h> #include <math.h> - template <typename U, typename V> gb_inline U bit_cast(V &v) { return reinterpret_cast<U &>(v); } @@ -145,6 +143,9 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) { #define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++) +#include "range_cache.cpp" + + u64 fnv64a(void const *data, isize len) { u8 const *bytes = cast(u8 const *)data; @@ -331,7 +332,7 @@ void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) { #include "ptr_set.cpp" #include "string_set.cpp" #include "priority_queue.cpp" - +#include "thread_pool.cpp" gb_global String global_module_path = {0}; @@ -873,7 +874,6 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) { info.size = size; info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; array_add(fi, info); - } while (FindNextFileW(find_file, &file_data)); if (fi->count == 0) { diff --git a/src/entity.cpp b/src/entity.cpp index 3318eac24..bbea68e8b 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -48,7 +48,8 @@ enum EntityFlag { EntityFlag_NotExported = 1<<14, EntityFlag_Static = 1<<16, - // EntityFlag_Reference = 1<<17, + + EntityFlag_ImplicitReference = 1<<17, // NOTE(bill): equivalent to `const &` in C++ EntityFlag_CVarArg = 1<<20, EntityFlag_AutoCast = 1<<21, @@ -183,10 +184,11 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) { } String name = e->token.string; - if (name.len == 0) { - return false; + switch (name.len) { + case 0: return false; + case 1: return name[0] != '_'; } - return name[0] != '_'; + return true; } bool entity_has_deferred_procedure(Entity *e) { @@ -219,12 +221,13 @@ Entity *alloc_entity_variable(Scope *scope, Token token, Type *type, bool is_imm return entity; } -Entity *alloc_entity_using_variable(Entity *parent, Token token, Type *type) { +Entity *alloc_entity_using_variable(Entity *parent, Token token, Type *type, Ast *using_expr) { GB_ASSERT(parent != nullptr); token.pos = parent->token.pos; Entity *entity = alloc_entity(Entity_Variable, parent->scope, token, type); entity->using_parent = parent; entity->parent_proc_decl = parent->parent_proc_decl; + entity->using_expr = using_expr; entity->flags |= EntityFlag_Using; entity->flags |= EntityFlag_Used; entity->state = EntityState_Resolved; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 428690291..42b22c8ef 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -12,6 +12,19 @@ bool are_types_identical(Type *x, Type *y); struct Complex128 { f64 real, imag; }; +struct Quaternion256 { + f64 imag, jmag, kmag, real; +}; + +Quaternion256 quaternion256_inverse(Quaternion256 x) { + f64 invmag2 = 1.0 / (x.real*x.real + x.imag*x.imag + x.jmag*x.jmag + x.kmag*x.kmag); + x.real = +x.real * invmag2; + x.imag = -x.imag * invmag2; + x.jmag = -x.jmag * invmag2; + x.kmag = -x.kmag * invmag2; + return x; +} + enum ExactValueKind { ExactValue_Invalid, @@ -21,9 +34,11 @@ enum ExactValueKind { ExactValue_Integer, ExactValue_Float, ExactValue_Complex, + ExactValue_Quaternion, ExactValue_Pointer, ExactValue_Compound, // TODO(bill): Is this good enough? ExactValue_Procedure, // TODO(bill): Is this good enough? + ExactValue_Typeid, ExactValue_Count, }; @@ -37,8 +52,10 @@ struct ExactValue { f64 value_float; i64 value_pointer; Complex128 value_complex; + Quaternion256 value_quaternion; Ast * value_compound; Ast * value_procedure; + Type * value_typeid; }; }; @@ -53,25 +70,22 @@ HashKey hash_exact_value(ExactValue v) { return hash_integer(u64(v.value_bool)); case ExactValue_String: return hash_string(v.value_string); - case ExactValue_Integer: { - u64 *d = big_int_ptr(&v.value_integer); - u64 x = 0; - for (i32 i = 0; i < v.value_integer.len; i++) { - x |= d[i]; - } - return hash_integer(x); - } + case ExactValue_Integer: + return hashing_proc(big_int_ptr(&v.value_integer), v.value_integer.len * gb_size_of(u64)); case ExactValue_Float: return hash_f64(v.value_float); case ExactValue_Pointer: return hash_integer(v.value_pointer); case ExactValue_Complex: return hashing_proc(&v.value_complex, gb_size_of(Complex128)); - + case ExactValue_Quaternion: + return hashing_proc(&v.value_quaternion, gb_size_of(Quaternion256)); case ExactValue_Compound: return hash_pointer(v.value_compound); case ExactValue_Procedure: return hash_pointer(v.value_procedure); + case ExactValue_Typeid: + return hash_pointer(v.value_typeid); } return hashing_proc(&v, gb_size_of(ExactValue)); @@ -122,6 +136,15 @@ ExactValue exact_value_complex(f64 real, f64 imag) { return result; } +ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) { + ExactValue result = {ExactValue_Quaternion}; + result.value_quaternion.real = real; + result.value_quaternion.imag = imag; + result.value_quaternion.jmag = jmag; + result.value_quaternion.kmag = kmag; + return result; +} + ExactValue exact_value_pointer(i64 ptr) { ExactValue result = {ExactValue_Pointer}; result.value_pointer = ptr; @@ -135,6 +158,13 @@ ExactValue exact_value_procedure(Ast *node) { } +ExactValue exact_value_typeid(Type *type) { + ExactValue result = {ExactValue_Typeid}; + result.value_typeid = type; + return result; +} + + ExactValue exact_value_integer_from_string(String const &string) { ExactValue result = {ExactValue_Integer}; big_int_from_string(&result.value_integer, string); @@ -259,14 +289,16 @@ ExactValue exact_value_from_basic_literal(Token token) { str.len--; // Ignore the 'i|j|k' f64 imag = float_from_string(str); - if (last_rune == 'i') { - return exact_value_complex(0, imag); + switch (last_rune) { + case 'i': return exact_value_complex(0, imag); + case 'j': return exact_value_quaternion(0, 0, imag, 0); + case 'k': return exact_value_quaternion(0, 0, 0, imag); + default: GB_PANIC("Invalid imaginary basic literal"); } } 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 exact_value_i64(r); } default: @@ -324,11 +356,26 @@ ExactValue exact_value_to_complex(ExactValue v) { return exact_value_complex(v.value_float, 0); case ExactValue_Complex: return v; + // case ExactValue_Quaternion: + // return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} +ExactValue exact_value_to_quaternion(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(big_int_to_f64(&v.value_integer), 0, 0, 0); + case ExactValue_Float: + return exact_value_quaternion(v.value_float, 0, 0, 0); + case ExactValue_Complex: + return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0); + case ExactValue_Quaternion: + return v; } ExactValue r = {ExactValue_Invalid}; return r; } - ExactValue exact_value_real(ExactValue v) { switch (v.kind) { @@ -337,6 +384,8 @@ ExactValue exact_value_real(ExactValue v) { return v; case ExactValue_Complex: return exact_value_float(v.value_complex.real); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.real); } ExactValue r = {ExactValue_Invalid}; return r; @@ -349,6 +398,34 @@ ExactValue exact_value_imag(ExactValue v) { return exact_value_i64(0); case ExactValue_Complex: return exact_value_float(v.value_complex.imag); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.imag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_jmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + case ExactValue_Float: + case ExactValue_Complex: + return exact_value_i64(0); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.jmag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_kmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + case ExactValue_Float: + case ExactValue_Complex: + return exact_value_i64(0); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.kmag); } ExactValue r = {ExactValue_Invalid}; return r; @@ -367,6 +444,32 @@ ExactValue exact_value_make_imag(ExactValue v) { return r; } +ExactValue exact_value_make_jmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(0, 0, exact_value_to_float(v).value_float, 0); + case ExactValue_Float: + return exact_value_quaternion(0, 0, v.value_float, 0); + default: + GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'"); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_make_kmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(0, 0, 0, exact_value_to_float(v).value_float); + case ExactValue_Float: + return exact_value_quaternion(0, 0, 0, v.value_float); + default: + GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'"); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + i64 exact_value_to_i64(ExactValue v) { v = exact_value_to_integer(v); if (v.kind == ExactValue_Integer) { @@ -395,6 +498,7 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, case ExactValue_Integer: case ExactValue_Float: case ExactValue_Complex: + case ExactValue_Quaternion: return v; } break; @@ -419,6 +523,13 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, f64 imag = v.value_complex.imag; return exact_value_complex(-real, -imag); } + case ExactValue_Quaternion: { + f64 real = v.value_quaternion.real; + f64 imag = v.value_quaternion.imag; + f64 jmag = v.value_quaternion.jmag; + f64 kmag = v.value_quaternion.kmag; + return exact_value_quaternion(-real, -imag, -jmag, -kmag); + } } break; } @@ -469,8 +580,10 @@ i32 exact_value_order(ExactValue const &v) { return 3; case ExactValue_Complex: return 4; - case ExactValue_Pointer: + case ExactValue_Quaternion: return 5; + case ExactValue_Pointer: + return 6; default: GB_PANIC("How'd you get here? Invalid Value.kind"); @@ -491,7 +604,7 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Bool: case ExactValue_String: - case ExactValue_Complex: + case ExactValue_Quaternion: return; case ExactValue_Integer: @@ -505,6 +618,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Complex: *x = exact_value_complex(big_int_to_f64(&x->value_integer), 0); return; + case ExactValue_Quaternion: + *x = exact_value_quaternion(big_int_to_f64(&x->value_integer), 0, 0, 0); + return; } break; @@ -515,6 +631,17 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Complex: *x = exact_value_to_complex(*x); return; + case ExactValue_Quaternion: + *x = exact_value_to_quaternion(*x); + return; + } + break; + + case ExactValue_Complex: + switch (y->kind) { + case ExactValue_Quaternion: + *x = exact_value_to_quaternion(*x); + return; } break; } @@ -612,6 +739,56 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) break; } + case ExactValue_Quaternion: { + y = exact_value_to_quaternion(y); + f64 xr = x.value_quaternion.real; + f64 xi = x.value_quaternion.imag; + f64 xj = x.value_quaternion.jmag; + f64 xk = x.value_quaternion.kmag; + f64 yr = y.value_quaternion.real; + f64 yi = y.value_quaternion.imag; + f64 yj = y.value_quaternion.jmag; + f64 yk = y.value_quaternion.kmag; + + + f64 real = 0; + f64 imag = 0; + f64 jmag = 0; + f64 kmag = 0; + + switch (op) { + case Token_Add: + real = xr + yr; + imag = xi + yi; + jmag = xj + yj; + kmag = xk + yk; + break; + case Token_Sub: + real = xr - yr; + imag = xi - yi; + jmag = xj - yj; + kmag = xk - yk; + break; + case Token_Mul: + imag = xr * yi + xi * yr + xj * yk - xk * yj; + jmag = xr * yj - xi * yk + xj * yr + xk * yi; + kmag = xr * yk + xi * yj - xj * yi + xk * yr; + real = xr * yr - xi * yi - xj * yj - xk * yk; + break; + case Token_Quo: { + f64 invmag2 = 1.0 / (yr*yr + yi*yi + yj*yj + yk*yk); + imag = (xr * -yi + xi * +yr + xj * -yk - xk * -yj) * invmag2; + jmag = (xr * -yj - xi * -yk + xj * +yr + xk * -yi) * invmag2; + kmag = (xr * -yk + xi * -yj - xj * -yi + xk * +yr) * invmag2; + real = (xr * +yr - xi * -yi - xj * -yj - xk * -yk) * invmag2; + break; + } + default: goto error; + } + return exact_value_quaternion(real, imag, jmag, kmag); + break; + } + case ExactValue_String: { if (op != Token_Add) goto error; @@ -647,6 +824,10 @@ gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue const &x, ExactV return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_increment_one(ExactValue const &x) { + return exact_binary_operator_value(Token_Add, x, exact_value_i64(1)); +} + i32 cmp_f64(f64 a, f64 b) { return (a > b) - (a < b); @@ -719,8 +900,61 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { } break; } + + case ExactValue_Typeid: + switch (op) { + case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid); + case Token_NotEq: return !are_types_identical(x.value_typeid, y.value_typeid); + } + break; } GB_PANIC("Invalid comparison"); return false; } + + +gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize string_limit=36) { + switch (v.kind) { + case ExactValue_Invalid: + return str; + case ExactValue_Bool: + return gb_string_appendc(str, v.value_bool ? "true" : "false"); + case ExactValue_String: { + String s = quote_to_ascii(heap_allocator(), v.value_string); + string_limit = gb_max(string_limit, 36); + if (s.len <= string_limit) { + str = gb_string_append_length(str, s.text, s.len); + } else { + isize n = string_limit/5; + str = gb_string_append_length(str, s.text, n); + str = gb_string_append_fmt(str, "\"..%lld chars..\"", s.len-(2*n)); + str = gb_string_append_length(str, s.text+s.len-n, n); + } + gb_free(heap_allocator(), s.text); + return str; + } + case ExactValue_Integer: { + String s = big_int_to_string(heap_allocator(), &v.value_integer); + str = gb_string_append_length(str, s.text, s.len); + gb_free(heap_allocator(), s.text); + return str; + } + case ExactValue_Float: + return gb_string_append_fmt(str, "%f", v.value_float); + case ExactValue_Complex: + return gb_string_append_fmt(str, "%f+%fi", v.value_complex.real, v.value_complex.imag); + + case ExactValue_Pointer: + return str; + case ExactValue_Compound: + return str; + case ExactValue_Procedure: + return str; + } + return str; +}; + +gbString exact_value_to_string(ExactValue const &v, isize string_limit=36) { + return write_exact_value_to_string(gb_string_make(heap_allocator(), ""), v, string_limit); +} diff --git a/src/gb/gb.h b/src/gb/gb.h index adeb554b2..60303729f 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -918,7 +918,7 @@ GB_DEF void gb_lfence (void); #if defined(GB_SYSTEM_WINDOWS) -typedef struct gbSemaphore { void *win32_handle; } gbSemaphore; +typedef struct gbSemaphore { void *win32_handle;} gbSemaphore; #elif defined(GB_SYSTEM_OSX) typedef struct gbSemaphore { semaphore_t osx_handle; } gbSemaphore; #elif defined(GB_SYSTEM_UNIX) @@ -930,7 +930,7 @@ typedef struct gbSemaphore { sem_t unix_handle; } gbSemaphore; GB_DEF void gb_semaphore_init (gbSemaphore *s); GB_DEF void gb_semaphore_destroy(gbSemaphore *s); GB_DEF void gb_semaphore_post (gbSemaphore *s, i32 count); -GB_DEF void gb_semaphore_release(gbSemaphore *s); // NOTE(bill): gb_semaphore_post(s, 1) +GB_DEF void gb_semaphore_release(gbSemaphore *s); GB_DEF void gb_semaphore_wait (gbSemaphore *s); @@ -975,10 +975,10 @@ typedef struct gbThread { pthread_t posix_handle; #endif - gbThreadProc *proc; - void * user_data; - isize user_index; - isize return_value; + gbThreadProc * proc; + void * user_data; + isize user_index; + isize volatile return_value; gbSemaphore semaphore; isize stack_size; @@ -4588,10 +4588,18 @@ gb_inline void gb_lfence(void) { gb_inline void gb_semaphore_release(gbSemaphore *s) { gb_semaphore_post(s, 1); } #if defined(GB_SYSTEM_WINDOWS) - gb_inline void gb_semaphore_init (gbSemaphore *s) { s->win32_handle = CreateSemaphoreA(NULL, 0, I32_MAX, NULL); } - gb_inline void gb_semaphore_destroy(gbSemaphore *s) { CloseHandle(s->win32_handle); } - gb_inline void gb_semaphore_post (gbSemaphore *s, i32 count) { ReleaseSemaphore(s->win32_handle, count, NULL); } - gb_inline void gb_semaphore_wait (gbSemaphore *s) { WaitForSingleObject(s->win32_handle, INFINITE); } + gb_inline void gb_semaphore_init(gbSemaphore *s) { + s->win32_handle = CreateSemaphoreA(NULL, 0, I32_MAX, NULL); + } + gb_inline void gb_semaphore_destroy(gbSemaphore *s) { + CloseHandle(s->win32_handle); + } + gb_inline void gb_semaphore_post(gbSemaphore *s, i32 count) { + ReleaseSemaphore(s->win32_handle, count, NULL); + } + gb_inline void gb_semaphore_wait(gbSemaphore *s) { + WaitForSingleObjectEx(s->win32_handle, INFINITE, FALSE); + } #elif defined(GB_SYSTEM_OSX) gb_inline void gb_semaphore_init (gbSemaphore *s) { semaphore_create(mach_task_self(), &s->osx_handle, SYNC_POLICY_FIFO, 0); } @@ -8975,7 +8983,7 @@ gb_inline void gb_exit(u32 code) { exit(code); } gb_inline void gb_yield(void) { #if defined(GB_SYSTEM_WINDOWS) - Sleep(0); + YieldProcessor(); #else sched_yield(); #endif diff --git a/src/ir.cpp b/src/ir.cpp index 82f9fa755..e7317a960 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -147,6 +147,7 @@ struct irProcedure { Array<irContextData> context_stack; + i32 parameter_count; irValue *return_ptr_hint_value; Ast * return_ptr_hint_ast; @@ -174,6 +175,7 @@ gbAllocator ir_allocator(void) { #define IR_TYPE_INFO_NAMES_NAME "__$type_info_names_data" #define IR_TYPE_INFO_OFFSETS_NAME "__$type_info_offsets_data" #define IR_TYPE_INFO_USINGS_NAME "__$type_info_usings_data" +#define IR_TYPE_INFO_TAGS_NAME "__$type_info_tags_data" #define IR_INSTR_KINDS \ @@ -424,6 +426,7 @@ enum irParamPasskind { irParamPass_Integer, // Pass as an integer of the same size irParamPass_ConstRef, // Pass as a pointer but the value is immutable irParamPass_BitCast, // Pass by value and bit cast to the correct type + irParamPass_Tuple, // Pass across multiple parameters (System V AMD64, up to 2) }; struct irValueParam { @@ -432,6 +435,7 @@ struct irValueParam { Entity * entity; Type * type; Type * original_type; + i32 index; Array<irValue *> referrers; }; @@ -913,15 +917,18 @@ irValue *ir_value_global(Entity *e, irValue *value) { if (value) value->uses += 1; return v; } -irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type) { +irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type, i32 index) { irValue *v = ir_alloc_value(irValue_Param); v->Param.kind = irParamPass_Value; v->Param.parent = parent; - v->Param.entity = e; - v->Param.original_type = e->type; + if (e != nullptr) { + v->Param.entity = e; + v->Param.original_type = e->type; + } v->Param.type = abi_type; + v->Param.index = index; - if (abi_type != e->type) { + if (e != nullptr && abi_type != e->type) { if (is_type_pointer(abi_type)) { GB_ASSERT(e->kind == Entity_Variable); v->Param.kind = irParamPass_Pointer; @@ -934,8 +941,12 @@ irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type) { v->Param.kind = irParamPass_Value; } else if (is_type_simd_vector(abi_type)) { v->Param.kind = irParamPass_BitCast; + } else if (is_type_float(abi_type)) { + v->Param.kind = irParamPass_BitCast; + } else if (is_type_tuple(abi_type)) { + v->Param.kind = irParamPass_Tuple; } else { - GB_PANIC("Invalid abi type pass kind"); + GB_PANIC("Invalid abi type pass kind %s", type_to_string(abi_type)); } } array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here @@ -1428,7 +1439,7 @@ irValue *ir_value_procedure(irModule *m, Entity *entity, Type *type, Ast *type_e Type *t = base_type(type); GB_ASSERT(is_type_proc(t)); - array_init(&v->Proc.params, ir_allocator(), 0, t->Proc.param_count); + array_init(&v->Proc.params, heap_allocator(), 0, t->Proc.param_count); return v; } @@ -1498,7 +1509,9 @@ void ir_start_block(irProcedure *proc, irBlock *block) { } - +irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t); +irValue *ir_address_from_load_or_generate_local(irProcedure *proc, irValue *val); +irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index); @@ -1539,6 +1552,7 @@ irValue *ir_add_module_constant(irModule *m, Type *type, ExactValue value) { if (count == 0) { return ir_value_nil(type); } + count = gb_max(cl->max_count, count); Type *elem = base_type(type)->Slice.elem; Type *t = alloc_type_array(elem, count); irValue *backing_array = ir_add_module_constant(m, t, value); @@ -1712,9 +1726,12 @@ irValue *ir_add_global_generated(irModule *m, Type *type, irValue *value) { irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i32 index) { - irValue *v = ir_value_param(proc, e, abi_type); + irValue *v = ir_value_param(proc, e, abi_type, index); + array_add(&proc->params, v); irValueParam *p = &v->Param; + irValue *res = nullptr; + ir_push_debug_location(proc->module, e ? e->identifier : nullptr, proc->debug_scope, e); defer (ir_pop_debug_location(proc->module)); @@ -1749,6 +1766,24 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i ir_emit_store(proc, l, x); return x; } + case irParamPass_Tuple: { + irValue *l = ir_add_local(proc, e, expr, true, index); + Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); + irValue *ptr = ir_emit_bitcast(proc, l, alloc_type_pointer(st)); + if (abi_type->Tuple.variables.count > 0) { + array_pop(&proc->params); + } + for_array(i, abi_type->Tuple.variables) { + Type *t = abi_type->Tuple.variables[i]->type; + + irValue *elem = ir_value_param(proc, nullptr, t, index+cast(i32)i); + array_add(&proc->params, elem); + + irValue *dst = ir_emit_struct_ep(proc, ptr, cast(i32)i); + ir_emit_store(proc, dst, elem); + } + return ir_emit_load(proc, l); + } } @@ -1872,10 +1907,12 @@ irDebugEncoding ir_debug_encoding_for_basic(BasicKind kind) { case Basic_string: case Basic_any: case Basic_rawptr: + case Basic_quaternion128: + case Basic_quaternion256: break; // not a "DIBasicType" } - GB_PANIC("Unreachable"); + GB_PANIC("Unreachable %d", kind); return irDebugBasicEncoding_Invalid; } @@ -2221,27 +2258,11 @@ irDebugInfo *ir_add_debug_info_type_complex(irModule *module, Type *type) { di->CompositeType.tag = irDebugBasicEncoding_structure_type; di->CompositeType.size = ir_debug_size_bits(type); - Type *field_type = nullptr; - if (type->Basic.kind == Basic_complex64) { - field_type = t_f32; - } else if (type->Basic.kind == Basic_complex128) { - field_type = t_f64; - } else { - GB_PANIC("Unreachable"); - } + Type *field_type = base_complex_elem_type(type); - // Field "real" - irDebugInfo *real_di = ir_add_debug_info_field_internal(module, str_lit("real"), field_type, - 0, - nullptr, - di); + irDebugInfo *real_di = ir_add_debug_info_field_internal(module, str_lit("real"), field_type, 0*cast(i32)type_size_of(field_type), nullptr, di); + irDebugInfo *imag_di = ir_add_debug_info_field_internal(module, str_lit("imag"), field_type, 1*cast(i32)type_size_of(field_type), nullptr, di); map_set(&module->debug_info, hash_pointer(real_di), real_di); - - // Field "imag" - irDebugInfo *imag_di = ir_add_debug_info_field_internal(module, str_lit("imag"), field_type, - real_di->DerivedType.size, - nullptr, - di); map_set(&module->debug_info, hash_pointer(imag_di), imag_di); irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, 2); @@ -2253,6 +2274,40 @@ irDebugInfo *ir_add_debug_info_type_complex(irModule *module, Type *type) { return di; } +irDebugInfo *ir_add_debug_info_type_quaternion(irModule *module, Type *type) { + GB_ASSERT(type->kind == Type_Basic && is_type_quaternion(type)); + + irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType); + map_set(&module->debug_info, hash_type(type), di); + + di->CompositeType.name = type->Basic.name; + di->CompositeType.tag = irDebugBasicEncoding_structure_type; + di->CompositeType.size = ir_debug_size_bits(type); + + Type *field_type = base_complex_elem_type(type); + + // @QuaternionLayout + irDebugInfo *imag_di = ir_add_debug_info_field_internal(module, str_lit("imag"), field_type, 0*cast(i32)type_size_of(field_type), nullptr, di); + irDebugInfo *jmag_di = ir_add_debug_info_field_internal(module, str_lit("jmag"), field_type, 1*cast(i32)type_size_of(field_type), nullptr, di); + irDebugInfo *kmag_di = ir_add_debug_info_field_internal(module, str_lit("kmag"), field_type, 2*cast(i32)type_size_of(field_type), nullptr, di); + irDebugInfo *real_di = ir_add_debug_info_field_internal(module, str_lit("real"), field_type, 3*cast(i32)type_size_of(field_type), nullptr, di); + + map_set(&module->debug_info, hash_pointer(imag_di), imag_di); + map_set(&module->debug_info, hash_pointer(jmag_di), jmag_di); + map_set(&module->debug_info, hash_pointer(kmag_di), kmag_di); + map_set(&module->debug_info, hash_pointer(real_di), real_di); + + irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, 4); + array_add(&elements_di->DebugInfoArray.elements, imag_di); + array_add(&elements_di->DebugInfoArray.elements, jmag_di); + array_add(&elements_di->DebugInfoArray.elements, kmag_di); + array_add(&elements_di->DebugInfoArray.elements, real_di); + di->CompositeType.elements = elements_di; + map_set(&module->debug_info, hash_pointer(elements_di), elements_di); + + return di; +} + irDebugInfo *ir_add_debug_info_proc_type(irModule *module, Type *type) { GB_ASSERT(type->kind == Type_Proc); @@ -2377,10 +2432,14 @@ irDebugInfo *ir_add_debug_info_type(irModule *module, Type *type, Entity *e, irD if (type->kind == Type_Basic) { switch (type->Basic.kind) { // Composite basic types - case Basic_complex64: - case Basic_complex128: return ir_add_debug_info_type_complex(module, type); - case Basic_string: return ir_add_debug_info_type_string(module, scope, e, type); - case Basic_any: return ir_add_debug_info_type_any(module); + case Basic_complex64: case Basic_complex128: + return ir_add_debug_info_type_complex(module, type); + case Basic_quaternion128: case Basic_quaternion256: + return ir_add_debug_info_type_quaternion(module, type); + case Basic_string: + return ir_add_debug_info_type_string(module, scope, e, type); + case Basic_any: + return ir_add_debug_info_type_any(module); // Derived basic types case Basic_cstring: @@ -2920,10 +2979,6 @@ void ir_emit_unreachable(irProcedure *proc) { ir_emit(proc, ir_instr_unreachable(proc)); } -irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t); -irValue *ir_address_from_load_or_generate_local(irProcedure *proc, irValue *val); -irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index); - irValue *ir_get_package_value(irModule *m, String package_name, String entity_name) { AstPackage *rt_pkg = get_core_package(m->info, package_name); @@ -2973,7 +3028,7 @@ Array<irValue *> ir_value_to_array(irProcedure *p, irValue *value) { } -irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, ProcInlining inlining = ProcInlining_none, bool use_return_ptr_hint = false) { +irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &args, ProcInlining inlining = ProcInlining_none, bool use_return_ptr_hint = false) { Type *pt = base_type(ir_type(value)); GB_ASSERT(pt->kind == Type_Proc); Type *results = pt->Proc.results; @@ -2983,6 +3038,8 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro context_ptr = ir_find_or_generate_context_ptr(p); } + set_procedure_abi_types(heap_allocator(), pt); + bool is_c_vararg = pt->Proc.c_vararg; isize param_count = pt->Proc.param_count; if (is_c_vararg) { @@ -2991,9 +3048,13 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro } else { GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count); } + + auto processed_args = array_make<irValue *>(heap_allocator(), 0, args.count); + for (isize i = 0; i < param_count; i++) { Entity *e = pt->Proc.params->Tuple.variables[i]; if (e->kind != Entity_Variable) { + array_add(&processed_args, args[i]); continue; } GB_ASSERT(e->flags & EntityFlag_Param); @@ -3003,21 +3064,32 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro Type *arg_type = ir_type(args[i]); if (are_types_identical(arg_type, new_type)) { // NOTE(bill): Done + array_add(&processed_args, args[i]); } else if (!are_types_identical(original_type, new_type)) { - if (is_type_pointer(new_type) && !is_type_pointer(original_type)) { - if (e->flags&EntityFlag_Value) { - args[i] = ir_address_from_load_or_generate_local(p, args[i]); + if (e->flags&EntityFlag_ImplicitReference) { + array_add(&processed_args, ir_address_from_load_or_generate_local(p, args[i])); } else if (!is_type_pointer(arg_type)) { - args[i] = ir_copy_value_to_ptr(p, args[i], original_type, 16); + array_add(&processed_args, ir_copy_value_to_ptr(p, args[i], original_type, 16)); } } else if (is_type_integer(new_type)) { - args[i] = ir_emit_transmute(p, args[i], new_type); + array_add(&processed_args, ir_emit_transmute(p, args[i], new_type)); } else if (new_type == t_llvm_bool) { - args[i] = ir_emit_conv(p, args[i], new_type); + array_add(&processed_args, ir_emit_conv(p, args[i], new_type)); } else if (is_type_simd_vector(new_type)) { - args[i] = ir_emit_bitcast(p, args[i], new_type); + array_add(&processed_args, ir_emit_bitcast(p, args[i], new_type)); + } else if (is_type_tuple(new_type)) { + Type *abi_type = pt->Proc.abi_compat_params[i]; + Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); + irValue *x = ir_emit_transmute(p, args[i], st); + for (isize j = 0; j < new_type->Tuple.variables.count; j++) { + irValue *xx = ir_emit_struct_ev(p, x, cast(i32)j); + array_add(&processed_args, xx); + } } + } else { + irValue *x = ir_emit_conv(p, args[i], new_type); + array_add(&processed_args, x); } } @@ -3042,10 +3114,10 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro return_ptr = ir_add_local_generated(p, rt, true); } GB_ASSERT(is_type_pointer(ir_type(return_ptr))); - ir_emit(p, ir_instr_call(p, value, return_ptr, args, nullptr, context_ptr, inlining)); + ir_emit(p, ir_instr_call(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining)); result = ir_emit_load(p, return_ptr); } else { - result = ir_emit(p, ir_instr_call(p, value, nullptr, args, abi_rt, context_ptr, inlining)); + result = ir_emit(p, ir_instr_call(p, value, nullptr, processed_args, abi_rt, context_ptr, inlining)); if (abi_rt != results) { result = ir_emit_transmute(p, result, rt); } @@ -3054,7 +3126,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro if (value->kind == irValue_Proc) { irProcedure *the_proc = &value->Proc; Entity *e = the_proc->entity; - if (entity_has_deferred_procedure(e)) { + if (e != nullptr && entity_has_deferred_procedure(e)) { DeferredProcedureKind kind = e->Procedure.deferred_procedure.kind; Entity *deferred_entity = e->Procedure.deferred_procedure.entity; irValue **deferred_found = map_get(&p->module->values, hash_entity(deferred_entity)); @@ -3653,12 +3725,12 @@ struct irLoopData { irBlock *loop; }; -irLoopData ir_loop_start(irProcedure *proc, isize count) { +irLoopData ir_loop_start(irProcedure *proc, isize count, Type *index_type=t_int) { irLoopData data = {}; irValue *max = ir_const_int(count); - data.idx_addr = ir_add_local_generated(proc, t_int, true); + data.idx_addr = ir_add_local_generated(proc, index_type, true); data.body = ir_new_block(proc, nullptr, "loop.body"); data.done = ir_new_block(proc, nullptr, "loop.done"); @@ -3728,7 +3800,7 @@ irValue *ir_emit_unary_arith(irProcedure *proc, TokenKind op, irValue *x, Type * ir_emit_store(proc, ir_emit_array_epi(proc, res, i), z); } } else { - auto loop_data = ir_loop_start(proc, count); + auto loop_data = ir_loop_start(proc, count, t_i32); irValue *e = ir_emit_load(proc, ir_emit_array_ep(proc, val, loop_data.idx)); irValue *z = ir_emit_unary_arith(proc, op, e, elem_type); @@ -3787,7 +3859,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * ir_emit_store(proc, ir_emit_array_epi(proc, res, i), z); } } else { - auto loop_data = ir_loop_start(proc, count); + auto loop_data = ir_loop_start(proc, count, t_i32); irValue *x = ir_emit_load(proc, ir_emit_array_ep(proc, lhs, loop_data.idx)); irValue *y = ir_emit_load(proc, ir_emit_array_ep(proc, rhs, loop_data.idx)); @@ -3853,6 +3925,60 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * return ir_emit_load(proc, res); } + if (is_type_quaternion(t_left)) { + ir_emit_comment(proc, str_lit("complex.arith.begin")); + defer (ir_emit_comment(proc, str_lit("complex.arith.end"))); + + right = ir_emit_conv(proc, right, t_left); + + Type *ft = base_complex_elem_type(t_left); + + if (op == Token_Add || op == Token_Sub) { + irValue *res = ir_add_local_generated(proc, type, false); // NOTE: initialized in full later + irValue *x0 = ir_emit_struct_ev(proc, left, 0); + irValue *x1 = ir_emit_struct_ev(proc, left, 1); + irValue *x2 = ir_emit_struct_ev(proc, left, 2); + irValue *x3 = ir_emit_struct_ev(proc, left, 3); + + irValue *y0 = ir_emit_struct_ev(proc, right, 0); + irValue *y1 = ir_emit_struct_ev(proc, right, 1); + irValue *y2 = ir_emit_struct_ev(proc, right, 2); + irValue *y3 = ir_emit_struct_ev(proc, right, 3); + + irValue *z0 = ir_emit_arith(proc, op, x0, y0, ft); + irValue *z1 = ir_emit_arith(proc, op, x1, y1, ft); + irValue *z2 = ir_emit_arith(proc, op, x2, y2, ft); + irValue *z3 = ir_emit_arith(proc, op, x3, y3, ft); + + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), z0); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), z1); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 2), z2); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 3), z3); + + return ir_emit_load(proc, res); + } else if (op == Token_Mul) { + auto args = array_make<irValue *>(heap_allocator(), 2); + args[0] = left; + args[1] = right; + + switch (8*type_size_of(ft)) { + case 32: return ir_emit_runtime_call(proc, "mul_quaternion128", args); + case 64: return ir_emit_runtime_call(proc, "mul_quaternion256", args); + default: GB_PANIC("Unknown float type"); break; + } + } else if (op == Token_Quo) { + auto args = array_make<irValue *>(heap_allocator(), 2); + args[0] = left; + args[1] = right; + + switch (8*type_size_of(ft)) { + case 32: return ir_emit_runtime_call(proc, "quo_quaternion128", args); + case 64: return ir_emit_runtime_call(proc, "quo_quaternion256", args); + default: GB_PANIC("Unknown float type"); break; + } + } + } + #if 0 if (op == Token_Add) { @@ -4018,6 +4144,15 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue } else if (is_type_typeid(t)) { irValue *invalid_typeid = ir_value_constant(t_typeid, exact_value_i64(0)); return ir_emit_comp(proc, op_kind, x, invalid_typeid); + } else if (is_type_bit_field(t)) { + auto args = array_make<irValue *>(heap_allocator(), 2); + irValue *lhs = ir_address_from_load_or_generate_local(proc, x); + args[0] = ir_emit_conv(proc, lhs, t_rawptr); + args[1] = ir_const_int(type_size_of(t)); + irValue *val = ir_emit_runtime_call(proc, "memory_compare_zero", args); + irValue *res = ir_emit_comp(proc, op_kind, val, v_zero); + return ir_emit_conv(proc, res, t_bool); + } return nullptr; } @@ -4105,6 +4240,9 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal if (op_kind == Token_NotEq) { res = v_false; cmp_op = Token_Or; + } else if (op_kind == Token_CmpEq) { + res = v_true; + cmp_op = Token_And; } bool inline_array_arith = type_size_of(tl) <= build_context.max_align; @@ -4112,28 +4250,43 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal if (inline_array_arith) { // inline + irValue *val = ir_add_local_generated(proc, t_bool, false); + ir_emit_store(proc, val, res); for (i32 i = 0; i < count; i++) { irValue *x = ir_emit_load(proc, ir_emit_array_epi(proc, lhs, i)); irValue *y = ir_emit_load(proc, ir_emit_array_epi(proc, rhs, i)); irValue *cmp = ir_emit_comp(proc, op_kind, x, y); - res = ir_emit_arith(proc, cmp_op, res, cmp, t_bool); - } - - return ir_emit_conv(proc, res, t_bool); - } else { - irValue *val = ir_add_local_generated(proc, t_bool, false); - ir_emit_store(proc, val, res); - auto loop_data = ir_loop_start(proc, count); - { - irValue *x = ir_emit_load(proc, ir_emit_array_ep(proc, lhs, loop_data.idx)); - irValue *y = ir_emit_load(proc, ir_emit_array_ep(proc, rhs, loop_data.idx)); - irValue *cmp = ir_emit_comp(proc, op_kind, x, y); - irValue *new_res = ir_emit_arith(proc, cmp_op, res, cmp, t_bool); + irValue *new_res = ir_emit_arith(proc, cmp_op, ir_emit_load(proc, val), cmp, t_bool); ir_emit_store(proc, val, ir_emit_conv(proc, new_res, t_bool)); } - ir_loop_end(proc, loop_data); return ir_emit_load(proc, val); + } else { + if (is_type_simple_compare(tl) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) { + // TODO(bill): Test to see if this is actually faster!!!! + auto args = array_make<irValue *>(heap_allocator(), 3); + args[0] = ir_emit_conv(proc, lhs, t_rawptr); + args[1] = ir_emit_conv(proc, rhs, t_rawptr); + args[2] = ir_const_int(type_size_of(tl)); + irValue *val = ir_emit_runtime_call(proc, "memory_compare", args); + irValue *res = ir_emit_comp(proc, op_kind, val, v_zero); + return ir_emit_conv(proc, res, t_bool); + } else { + irValue *val = ir_add_local_generated(proc, t_bool, false); + ir_emit_store(proc, val, res); + auto loop_data = ir_loop_start(proc, count, t_i32); + { + irValue *i = loop_data.idx; + irValue *x = ir_emit_load(proc, ir_emit_array_ep(proc, lhs, i)); + irValue *y = ir_emit_load(proc, ir_emit_array_ep(proc, rhs, i)); + irValue *cmp = ir_emit_comp(proc, op_kind, x, y); + irValue *new_res = ir_emit_arith(proc, cmp_op, ir_emit_load(proc, val), cmp, t_bool); + ir_emit_store(proc, val, ir_emit_conv(proc, new_res, t_bool)); + } + ir_loop_end(proc, loop_data); + + return ir_emit_load(proc, val); + } } } @@ -4161,7 +4314,7 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal } if (is_type_complex(a)) { - char *runtime_proc = ""; + char const *runtime_proc = ""; i64 sz = 8*type_size_of(a); switch (sz) { case 64: @@ -4185,6 +4338,31 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal return ir_emit_runtime_call(proc, runtime_proc, args); } + if (is_type_quaternion(a)) { + char const *runtime_proc = ""; + i64 sz = 8*type_size_of(a); + switch (sz) { + case 128: + switch (op_kind) { + case Token_CmpEq: runtime_proc = "quaternion128_eq"; break; + case Token_NotEq: runtime_proc = "quaternion128_ne"; break; + } + break; + case 256: + switch (op_kind) { + case Token_CmpEq: runtime_proc = "quaternion256_eq"; break; + case Token_NotEq: runtime_proc = "quaternion256_ne"; break; + } + break; + } + GB_ASSERT(runtime_proc != nullptr); + + auto args = array_make<irValue *>(ir_allocator(), 2); + args[0] = left; + args[1] = right; + return ir_emit_runtime_call(proc, runtime_proc, args); + } + if (is_type_bit_set(a)) { switch (op_kind) { case Token_Lt: @@ -4268,11 +4446,18 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) { case 0: result_type = alloc_type_pointer(ft); break; case 1: result_type = alloc_type_pointer(ft); break; } + } else if (is_type_quaternion(t)) { + Type *ft = base_complex_elem_type(t); + switch (index) { + case 0: result_type = alloc_type_pointer(ft); break; + case 1: result_type = alloc_type_pointer(ft); break; + case 2: result_type = alloc_type_pointer(ft); break; + case 3: result_type = alloc_type_pointer(ft); break; + } } else if (is_type_slice(t)) { switch (index) { case 0: result_type = alloc_type_pointer(alloc_type_pointer(t->Slice.elem)); break; case 1: result_type = alloc_type_pointer(t_int); break; - case 2: result_type = alloc_type_pointer(t_int); break; } } else if (is_type_string(t)) { switch (index) { @@ -4343,6 +4528,17 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { } break; } + case Basic_quaternion128: case Basic_quaternion256: + { + Type *ft = base_complex_elem_type(t); + switch (index) { + case 0: result_type = ft; break; + case 1: result_type = ft; break; + case 2: result_type = ft; break; + case 3: result_type = ft; break; + } + break; + } } break; case Type_Struct: @@ -4401,13 +4597,14 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, irValue *e, Selection sel) { if (is_type_pointer(type)) { type = type_deref(type); e = ir_emit_load(proc, e); - e = ir_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? + // e = ir_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? } type = core_type(type); if (is_type_raw_union(type)) { type = type->Struct.fields[index]->type; - e = ir_emit_conv(proc, e, alloc_type_pointer(type)); + GB_ASSERT(is_type_pointer(ir_type(e))); + e = ir_emit_bitcast(proc, e, alloc_type_pointer(type)); } else if (is_type_struct(type)) { type = type->Struct.fields[index]->type; e = ir_emit_struct_ep(proc, e, index); @@ -4725,6 +4922,8 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { ev = exact_value_to_float(ev); } else if (is_type_complex(dst)) { ev = exact_value_to_complex(ev); + } else if (is_type_quaternion(dst)) { + ev = exact_value_to_quaternion(ev); } else if (is_type_string(dst)) { // Handled elsewhere GB_ASSERT_MSG(ev.kind == ExactValue_String, "%d", ev.kind); @@ -4848,6 +5047,49 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { return ir_emit_load(proc, gen); } + if (is_type_quaternion(src) && is_type_quaternion(dst)) { + // @QuaternionLayout + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, false); + irValue *q0 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 0), ft); + irValue *q1 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 1), ft); + irValue *q2 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 2), ft); + irValue *q3 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 3), ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), q0); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 1), q1); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 2), q2); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), q3); + return ir_emit_load(proc, gen); + } + + if (is_type_float(src) && is_type_complex(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, value, ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), real); + return ir_emit_load(proc, gen); + } + if (is_type_float(src) && is_type_quaternion(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, value, ft); + // @QuaternionLayout + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), real); + return ir_emit_load(proc, gen); + } + if (is_type_complex(src) && is_type_quaternion(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 0), ft); + irValue *imag = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 1), ft); + // @QuaternionLayout + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), imag); + return ir_emit_load(proc, gen); + } + + + // float <-> integer if (is_type_float(src) && is_type_integer(dst)) { irConvKind kind = irConv_fptosi; @@ -5245,12 +5487,14 @@ gb_global irValue *ir_global_type_info_member_types = nullptr; gb_global irValue *ir_global_type_info_member_names = nullptr; gb_global irValue *ir_global_type_info_member_offsets = nullptr; gb_global irValue *ir_global_type_info_member_usings = nullptr; +gb_global irValue *ir_global_type_info_member_tags = nullptr; gb_global i32 ir_global_type_info_data_index = 0; gb_global i32 ir_global_type_info_member_types_index = 0; gb_global i32 ir_global_type_info_member_names_index = 0; gb_global i32 ir_global_type_info_member_offsets_index = 0; gb_global i32 ir_global_type_info_member_usings_index = 0; +gb_global i32 ir_global_type_info_member_tags_index = 0; isize ir_type_info_count(CheckerInfo *info) { return info->minimum_dependency_type_info_set.entries.count+1; @@ -5286,6 +5530,7 @@ enum Typeid_Kind : u8 { Typeid_Rune, Typeid_Float, Typeid_Complex, + Typeid_Quaternion, Typeid_String, Typeid_Boolean, Typeid_Any, @@ -5402,7 +5647,8 @@ irValue *ir_emit_logical_binary_expr(irProcedure *proc, TokenKind op, Ast *left, } ir_start_block(proc, rhs); - array_add(&edges, ir_build_expr(proc, right)); + irValue *edge = ir_build_expr(proc, right); + array_add(&edges, edge); ir_emit_jump(proc, done); ir_start_block(proc, done); @@ -5411,8 +5657,6 @@ irValue *ir_emit_logical_binary_expr(irProcedure *proc, TokenKind op, Ast *left, irValue *ir_emit_logical_binary_expr(irProcedure *proc, Ast *expr) { ast_node(be, BinaryExpr, expr); - irBlock *rhs = ir_new_block(proc, nullptr, "logical.cmp.rhs"); - irBlock *done = ir_new_block(proc, nullptr, "logical.cmp.done"); Type *type = type_of_expr(expr); type = default_type(type); @@ -6038,17 +6282,77 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu return ir_emit_load(proc, dst); } + case BuiltinProc_quaternion: { + ir_emit_comment(proc, str_lit("quaternion")); + irValue *real = ir_build_expr(proc, ce->args[0]); + irValue *imag = ir_build_expr(proc, ce->args[1]); + irValue *jmag = ir_build_expr(proc, ce->args[2]); + irValue *kmag = ir_build_expr(proc, ce->args[3]); + + // @QuaternionLayout + irValue *dst = ir_add_local_generated(proc, tv.type, false); + Type *ft = base_complex_elem_type(tv.type); + real = ir_emit_conv(proc, real, ft); + imag = ir_emit_conv(proc, imag, ft); + jmag = ir_emit_conv(proc, jmag, ft); + kmag = ir_emit_conv(proc, kmag, ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 0), imag); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 1), jmag); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 2), kmag); + + return ir_emit_load(proc, dst); + } + case BuiltinProc_real: { ir_emit_comment(proc, str_lit("real")); irValue *val = ir_build_expr(proc, ce->args[0]); - irValue *real = ir_emit_struct_ev(proc, val, 0); - return ir_emit_conv(proc, real, tv.type); + if (is_type_complex(ir_type(val))) { + irValue *real = ir_emit_struct_ev(proc, val, 0); + return ir_emit_conv(proc, real, tv.type); + } else if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *real = ir_emit_struct_ev(proc, val, 3); + return ir_emit_conv(proc, real, tv.type); + } + GB_PANIC("invalid type for real"); + return nullptr; } case BuiltinProc_imag: { ir_emit_comment(proc, str_lit("imag")); irValue *val = ir_build_expr(proc, ce->args[0]); - irValue *imag = ir_emit_struct_ev(proc, val, 1); - return ir_emit_conv(proc, imag, tv.type); + if (is_type_complex(ir_type(val))) { + irValue *imag = ir_emit_struct_ev(proc, val, 1); + return ir_emit_conv(proc, imag, tv.type); + } else if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 0); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for imag"); + return nullptr; + } + case BuiltinProc_jmag: { + ir_emit_comment(proc, str_lit("jmag")); + irValue *val = ir_build_expr(proc, ce->args[0]); + if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 1); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for jmag"); + return nullptr; + } + case BuiltinProc_kmag: { + ir_emit_comment(proc, str_lit("kmag")); + irValue *val = ir_build_expr(proc, ce->args[0]); + if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 2); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for kmag"); + return nullptr; } case BuiltinProc_conj: { @@ -6063,6 +6367,20 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu imag = ir_emit_unary_arith(proc, Token_Sub, imag, ir_type(imag)); ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), real); ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), imag); + } else if (is_type_quaternion(t)) { + // @QuaternionLayout + res = ir_add_local_generated(proc, tv.type, false); + irValue *real = ir_emit_struct_ev(proc, val, 3); + irValue *imag = ir_emit_struct_ev(proc, val, 0); + irValue *jmag = ir_emit_struct_ev(proc, val, 1); + irValue *kmag = ir_emit_struct_ev(proc, val, 2); + imag = ir_emit_unary_arith(proc, Token_Sub, imag, ir_type(imag)); + jmag = ir_emit_unary_arith(proc, Token_Sub, jmag, ir_type(jmag)); + kmag = ir_emit_unary_arith(proc, Token_Sub, kmag, ir_type(kmag)); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), imag); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), jmag); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 2), kmag); } return ir_emit_load(proc, res); } @@ -6133,7 +6451,16 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu return x; } ir_emit_comment(proc, str_lit("abs")); - if (is_type_complex(t)) { + if (is_type_quaternion(t)) { + i64 sz = 8*type_size_of(t); + auto args = array_make<irValue *>(ir_allocator(), 1); + args[0] = x; + switch (sz) { + case 128: return ir_emit_runtime_call(proc, "abs_quaternion128", args); + case 256: return ir_emit_runtime_call(proc, "abs_quaternion256", args); + } + GB_PANIC("Unknown complex type"); + } else if (is_type_complex(t)) { i64 sz = 8*type_size_of(t); auto args = array_make<irValue *>(ir_allocator(), 1); args[0] = x; @@ -6312,6 +6639,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { return ir_emit_conv(proc, x, tv.type); } + if (tv.value.kind == ExactValue_Typeid) { + irValue *v = ir_typeid(proc->module, tv.value.value_typeid); + return ir_emit_conv(proc, v, tv.type); + } + return ir_add_module_constant(proc->module, tv.type, tv.value); } @@ -6546,9 +6878,6 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case_end; case_ast_node(be, BinaryExpr, expr); - irValue *left = ir_build_expr(proc, be->left); - Type *type = default_type(tv.type); - switch (be->op.kind) { case Token_Add: case Token_Sub: @@ -6562,6 +6891,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case Token_AndNot: case Token_Shl: case Token_Shr: { + irValue *left = ir_build_expr(proc, be->left); + Type *type = default_type(tv.type); irValue *right = ir_build_expr(proc, be->right); return ir_emit_arith(proc, be->op.kind, left, right, type); } @@ -6573,10 +6904,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case Token_LtEq: case Token_Gt: case Token_GtEq: { + irValue *left = ir_build_expr(proc, be->left); + Type *type = default_type(tv.type); irValue *right = ir_build_expr(proc, be->right); irValue *cmp = ir_emit_comp(proc, be->op.kind, left, right); return ir_emit_conv(proc, cmp, type); - break; } case Token_CmpAnd: @@ -6586,6 +6918,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case Token_in: case Token_notin: { + irValue *left = ir_build_expr(proc, be->left); + Type *type = default_type(tv.type); irValue *right = ir_build_expr(proc, be->right); Type *rt = base_type(ir_type(right)); switch (rt->kind) { @@ -6687,7 +7021,34 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { } // NOTE(bill): Regular call - irValue *value = ir_build_expr(proc, ce->proc); + irValue *value = nullptr; + Ast *proc_expr = unparen_expr(ce->proc); + if (proc_expr->tav.mode == Addressing_Constant) { + ExactValue v = proc_expr->tav.value; + switch (v.kind) { + case ExactValue_Integer: + { + u64 u = big_int_to_u64(&v.value_integer); + irValue *x = ir_const_uintptr(u); + x = ir_emit_conv(proc, x, t_rawptr); + value = ir_emit_conv(proc, x, proc_expr->tav.type); + break; + } + case ExactValue_Pointer: + { + u64 u = cast(u64)v.value_pointer; + irValue *x = ir_const_uintptr(u); + x = ir_emit_conv(proc, x, t_rawptr); + value = ir_emit_conv(proc, x, proc_expr->tav.type); + break; + } + } + } + + if (value == nullptr) { + value = ir_build_expr(proc, proc_expr); + } + GB_ASSERT(value != nullptr); Type *proc_type_ = base_type(ir_type(value)); GB_ASSERT(proc_type_->kind == Type_Proc); @@ -7501,13 +7862,55 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { Ast *elem = cl->elems[i]; - if (ir_is_elem_const(proc->module, elem, et)) { - continue; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (ir_is_elem_const(proc->module, fv->value, et)) { + continue; + } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } + + irValue *value = ir_build_expr(proc, fv->value); + + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + irCompoundLitElemTempData data = {}; + data.value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } + + } else { + if (ir_is_elem_const(proc->module, elem, et)) { + continue; + } + irCompoundLitElemTempData data = {}; + data.expr = elem; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); } - irCompoundLitElemTempData data = {}; - data.expr = elem; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); } for_array(i, temp_data) { @@ -7522,12 +7925,15 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { defer (proc->return_ptr_hint_value = return_ptr_hint_value); defer (proc->return_ptr_hint_used = return_ptr_hint_used); + irValue *field_expr = temp_data[i].value; Ast *expr = temp_data[i].expr; proc->return_ptr_hint_value = temp_data[i].gep; proc->return_ptr_hint_ast = unparen_expr(expr); - irValue *field_expr = ir_build_expr(proc, expr); + if (field_expr == nullptr) { + field_expr = ir_build_expr(proc, expr); + } Type *t = ir_type(field_expr); GB_ASSERT(t->kind != Type_Tuple); irValue *ev = ir_emit_conv(proc, field_expr, et); @@ -7560,18 +7966,64 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { for_array(i, cl->elems) { Ast *elem = cl->elems[i]; - if (ir_is_elem_const(proc->module, elem, et)) { - continue; - } - irValue *field_expr = ir_build_expr(proc, elem); - Type *t = ir_type(field_expr); - GB_ASSERT(t->kind != Type_Tuple); - irValue *ev = ir_emit_conv(proc, field_expr, et); + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + + if (ir_is_elem_const(proc->module, fv->value, et)) { + continue; + } + + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } + + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); + + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } else { + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(fv->field->tav.value); - irCompoundLitElemTempData data = {}; - data.value = ev; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); + irValue *field_expr = ir_build_expr(proc, fv->value); + GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + + irValue *ev = ir_emit_conv(proc, field_expr, et); + + irCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } + } else { + if (ir_is_elem_const(proc->module, elem, et)) { + continue; + } + irValue *field_expr = ir_build_expr(proc, elem); + GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + + irValue *ev = ir_emit_conv(proc, field_expr, et); + + irCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); + } } for_array(i, temp_data) { @@ -7592,28 +8044,64 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (cl->elems.count == 0) { break; } - Type *elem = bt->DynamicArray.elem; + Type *et = bt->DynamicArray.elem; gbAllocator a = ir_allocator(); - irValue *size = ir_const_int(type_size_of(elem)); - irValue *align = ir_const_int(type_align_of(elem)); + irValue *size = ir_const_int(type_size_of(et)); + irValue *align = ir_const_int(type_align_of(et)); + + i64 item_count = gb_max(cl->max_count, cl->elems.count); { + auto args = array_make<irValue *>(a, 5); args[0] = ir_emit_conv(proc, v, t_rawptr); args[1] = size; args[2] = align; - args[3] = ir_const_int(2*cl->elems.count); + args[3] = ir_const_int(2*item_count); // TODO(bill): Is this too much waste? args[4] = ir_emit_source_code_location(proc, proc_name, pos); ir_emit_runtime_call(proc, "__dynamic_array_reserve", args); } - i64 item_count = cl->elems.count; - irValue *items = ir_generate_array(proc->module, elem, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr); + irValue *items = ir_generate_array(proc->module, et, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr); + + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - for_array(field_index, cl->elems) { - Ast *f = cl->elems[field_index]; - irValue *value = ir_emit_conv(proc, ir_build_expr(proc, f), elem); - irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); - ir_emit_store(proc, ep, value); + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); + + for (i64 k = lo; k < hi; k++) { + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)k); + ir_emit_store(proc, ep, value); + } + } else { + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + + i64 field_index = exact_value_to_i64(fv->field->tav.value); + + irValue *ev = ir_build_expr(proc, fv->value); + irValue *value = ir_emit_conv(proc, ev, et); + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); + ir_emit_store(proc, ep, value); + } + } else { + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, elem), et); + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)i); + ir_emit_store(proc, ep, value); + } } { @@ -7907,24 +8395,27 @@ void ir_build_constant_value_decl(irProcedure *proc, AstValueDecl *vd) { name = e->Procedure.link_name; } + HashKey key = hash_string(name); + irValue **prev_value = map_get(&proc->module->members, key); + if (prev_value != nullptr) { + // NOTE(bill): Don't do mutliple declarations in the IR + return; + } + + irValue *value = ir_value_procedure(proc->module, e, e->type, pl->type, pl->body, name); value->Proc.tags = pl->tags; value->Proc.inlining = pl->inlining; - ir_module_add_value(proc->module, e, value); - ir_build_proc(value, proc); - if (value->Proc.is_foreign || value->Proc.is_export) { - HashKey key = hash_string(name); - irValue **prev_value = map_get(&proc->module->members, key); - if (prev_value == nullptr) { - // NOTE(bill): Don't do mutliple declarations in the IR - map_set(&proc->module->members, key, value); - } + map_set(&proc->module->members, key, value); } else { array_add(&proc->children, &value->Proc); } + + ir_module_add_value(proc->module, e, value); + ir_build_proc(value, proc); } } } @@ -8806,6 +9297,150 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { ir_start_block(proc, done); case_end; + case_ast_node(rs, InlineRangeStmt, node); + ir_emit_comment(proc, str_lit("InlineRangeStmt")); + ir_open_scope(proc); // Open scope here + + Type *val0_type = nullptr; + Type *val1_type = nullptr; + if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) { + val0_type = type_of_expr(rs->val0); + } + if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) { + val1_type = type_of_expr(rs->val1); + } + + if (val0_type != nullptr) { + ir_add_local_for_identifier(proc, rs->val0, true); + } + if (val1_type != nullptr) { + ir_add_local_for_identifier(proc, rs->val1, true); + } + + irValue *val = nullptr; + irValue *key = nullptr; + irBlock *loop = nullptr; + irBlock *done = nullptr; + Ast *expr = unparen_expr(rs->expr); + + TypeAndValue tav = type_and_value_of_expr(expr); + + if (is_ast_range(expr)) { + + irAddr val0_addr = {}; + irAddr val1_addr = {}; + if (val0_type) val0_addr = ir_build_addr(proc, rs->val0); + if (val1_type) val1_addr = ir_build_addr(proc, rs->val1); + + TokenKind op = expr->BinaryExpr.op.kind; + Ast *start_expr = expr->BinaryExpr.left; + Ast *end_expr = expr->BinaryExpr.right; + GB_ASSERT(start_expr->tav.mode == Addressing_Constant); + GB_ASSERT(end_expr->tav.mode == Addressing_Constant); + + ExactValue start = start_expr->tav.value; + ExactValue end = end_expr->tav.value; + if (op == Token_Ellipsis) { // .. [start, end] + ExactValue index = exact_value_i64(0); + for (ExactValue val = start; + compare_exact_values(Token_LtEq, val, end); + val = exact_value_increment_one(val), index = exact_value_increment_one(index)) { + + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, val)); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, index)); + + ir_build_stmt(proc, rs->body); + } + } else if (op == Token_RangeHalf) { // ..< [start, end) + ExactValue index = exact_value_i64(0); + for (ExactValue val = start; + compare_exact_values(Token_Lt, val, end); + val = exact_value_increment_one(val), index = exact_value_increment_one(index)) { + + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, val)); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, index)); + + ir_build_stmt(proc, rs->body); + } + } + + + } else if (tav.mode == Addressing_Type) { + GB_ASSERT(is_type_enum(type_deref(tav.type))); + Type *et = type_deref(tav.type); + Type *bet = base_type(et); + + irAddr val0_addr = {}; + irAddr val1_addr = {}; + if (val0_type) val0_addr = ir_build_addr(proc, rs->val0); + if (val1_type) val1_addr = ir_build_addr(proc, rs->val1); + + for_array(i, bet->Enum.fields) { + Entity *field = bet->Enum.fields[i]; + GB_ASSERT(field->kind == Entity_Constant); + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, field->Constant.value)); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(i))); + + ir_build_stmt(proc, rs->body); + } + } else { + irAddr val0_addr = {}; + irAddr val1_addr = {}; + if (val0_type) val0_addr = ir_build_addr(proc, rs->val0); + if (val1_type) val1_addr = ir_build_addr(proc, rs->val1); + + GB_ASSERT(expr->tav.mode == Addressing_Constant); + + Type *t = base_type(expr->tav.type); + + + switch (t->kind) { + case Type_Basic: + GB_ASSERT(is_type_string(t)); + { + ExactValue value = expr->tav.value; + GB_ASSERT(value.kind == ExactValue_String); + String str = value.value_string; + Rune codepoint = 0; + isize offset = 0; + do { + isize width = gb_utf8_decode(str.text+offset, str.len-offset, &codepoint); + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, exact_value_i64(codepoint))); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(offset))); + ir_build_stmt(proc, rs->body); + + offset += width; + } while (offset < str.len); + } + break; + case Type_Array: + if (t->Array.count > 0) { + irValue *val = ir_build_expr(proc, expr); + irValue *val_addr = ir_address_from_load_or_generate_local(proc, val); + + for (i64 i = 0; i < t->Array.count; i++) { + if (val0_type) { + // NOTE(bill): Due to weird legacy issues in LLVM, this needs to be an i32 + irValue *elem = ir_emit_array_epi(proc, val_addr, cast(i32)i); + ir_addr_store(proc, val0_addr, ir_emit_load(proc, elem)); + } + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(i))); + + ir_build_stmt(proc, rs->body); + } + + } + break; + default: + GB_PANIC("Invalid inline for type"); + break; + } + } + + + ir_close_scope(proc, irDeferExit_Default, nullptr); + case_end; + case_ast_node(ss, SwitchStmt, node); ir_emit_comment(proc, str_lit("SwitchStmt")); if (ss->init != nullptr) { @@ -9084,6 +9719,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { //////////////////////////////////////////////////////////////// void ir_number_proc_registers(irProcedure *proc) { + // i32 reg_index = proc->parameter_count; i32 reg_index = 0; for_array(i, proc->blocks) { irBlock *b = proc->blocks[i]; @@ -9139,13 +9775,15 @@ void ir_begin_procedure_body(irProcedure *proc) { proc->entry_block = ir_new_block(proc, proc->type_expr, "entry"); ir_start_block(proc, proc->entry_block); + i32 parameter_index = 0; + if (proc->type->Proc.return_by_pointer) { // NOTE(bill): this must be the first parameter stored Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(proc->type->Proc.results)); Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; - irValue *param = ir_value_param(proc, e, ptr_type); + irValue *param = ir_value_param(proc, e, ptr_type, -1); param->Param.kind = irParamPass_Pointer; ir_module_add_value(proc->module, e, param); @@ -9174,13 +9812,19 @@ void ir_begin_procedure_body(irProcedure *proc) { Entity *e = params->variables[i]; if (e->kind != Entity_Variable) { + parameter_index += 1; continue; } Type *abi_type = proc->type->Proc.abi_compat_params[i]; - if (e->token.string != "" && !is_blank_ident(e->token)) { - irValue *param = ir_add_param(proc, e, name, abi_type, cast(i32)(i+1)); - array_add(&proc->params, param); + if (e->token.string != "") { + ir_add_param(proc, e, name, abi_type, parameter_index); + } + + if (is_type_tuple(abi_type)) { + parameter_index += cast(i32)abi_type->Tuple.variables.count; + } else { + parameter_index += 1; } } } else { @@ -9189,15 +9833,20 @@ void ir_begin_procedure_body(irProcedure *proc) { for_array(i, params->variables) { Entity *e = params->variables[i]; if (e->kind != Entity_Variable) { + parameter_index += 1; continue; } Type *abi_type = e->type; if (abi_types.count > 0) { abi_type = abi_types[i]; } - if (e->token.string != "" && !is_blank_ident(e->token)) { - irValue *param = ir_add_param(proc, e, nullptr, abi_type, cast(i32)(i+1)); - array_add(&proc->params, param); + if (e->token.string != "") { + ir_add_param(proc, e, nullptr, abi_type, parameter_index); + } + if (is_type_tuple(abi_type)) { + parameter_index += cast(i32)abi_type->Tuple.variables.count; + } else { + parameter_index += 1; } } } @@ -9239,11 +9888,13 @@ void ir_begin_procedure_body(irProcedure *proc) { if (proc->type->Proc.calling_convention == ProcCC_Odin) { Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); e->flags |= EntityFlag_NoAlias; - irValue *param = ir_value_param(proc, e, e->type); + irValue *param = ir_value_param(proc, e, e->type, -1); ir_module_add_value(proc->module, e, param); irContextData ctx = {param, proc->scope_index}; array_add(&proc->context_stack, ctx); } + + proc->parameter_count = parameter_index; } @@ -9370,6 +10021,8 @@ void ir_insert_code_before_proc(irProcedure* proc, irProcedure *parent) { void ir_build_proc(irValue *value, irProcedure *parent) { irProcedure *proc = &value->Proc; + set_procedure_abi_types(heap_allocator(), proc->type); + proc->parent = parent; if (proc->body != nullptr) { @@ -9561,13 +10214,27 @@ void ir_init_module(irModule *m, Checker *c) { map_set(&m->members, hash_string(name), g); ir_global_type_info_member_usings = g; } + + { + String name = str_lit(IR_TYPE_INFO_TAGS_NAME); + Entity *e = alloc_entity_variable(nullptr, make_token_ident(name), + alloc_type_array(t_string, count), false); + irValue *g = ir_value_global(e, nullptr); + ir_module_add_value(m, e, g); + map_set(&m->members, hash_string(name), g); + ir_global_type_info_member_tags = g; + } } } } { irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompileUnit); - di->CompileUnit.file = m->info->files.entries[0].value; // Zeroth is the init file + + GB_ASSERT(m->info->files.entries.count > 0); + AstFile *file = m->info->files.entries[0].value; + + di->CompileUnit.file = file; // Zeroth is the init file di->CompileUnit.producer = str_lit("odin"); map_set(&m->debug_info, hash_pointer(m), di); @@ -9586,6 +10253,13 @@ void ir_init_module(irModule *m, Checker *c) { array_init(&m->debug_location_stack, heap_allocator()); // TODO(lachsinc): ir_allocator() ?? } + + { + for_array(i, m->info->files.entries) { + AstFile *file = m->info->files.entries[i].value; + ir_add_debug_info_file(m, file); + } + } } void ir_destroy_module(irModule *m) { @@ -9694,6 +10368,11 @@ irValue *ir_type_info_member_usings_offset(irProcedure *proc, isize count) { ir_global_type_info_member_usings_index += cast(i32)count; return offset; } +irValue *ir_type_info_member_tags_offset(irProcedure *proc, isize count) { + irValue *offset = ir_emit_array_epi(proc, ir_global_type_info_member_tags, ir_global_type_info_member_tags_index); + ir_global_type_info_member_tags_index += cast(i32)count; + return offset; +} @@ -9832,6 +10511,11 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info tag = ir_emit_conv(proc, variant_ptr, t_type_info_complex_ptr); break; + case Basic_quaternion128: + case Basic_quaternion256: + tag = ir_emit_conv(proc, variant_ptr, t_type_info_quaternion_ptr); + break; + case Basic_rawptr: tag = ir_emit_conv(proc, variant_ptr, t_type_info_pointer_ptr); break; @@ -10039,9 +10723,9 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info irValue *is_packed = ir_const_bool(t->Struct.is_packed); irValue *is_raw_union = ir_const_bool(t->Struct.is_raw_union); irValue *is_custom_align = ir_const_bool(t->Struct.custom_align != 0); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), is_packed); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 5), is_raw_union); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_custom_align); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 5), is_packed); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_raw_union); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 7), is_custom_align); } isize count = t->Struct.fields.count; @@ -10050,6 +10734,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info irValue *memory_names = ir_type_info_member_names_offset (proc, count); irValue *memory_offsets = ir_type_info_member_offsets_offset(proc, count); irValue *memory_usings = ir_type_info_member_usings_offset (proc, count); + irValue *memory_tags = ir_type_info_member_tags_offset (proc, count); type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet for (isize source_index = 0; source_index < count; source_index++) { @@ -10065,7 +10750,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info irValue *index = ir_const_int(source_index); irValue *type_info = ir_emit_ptr_offset(proc, memory_types, index); irValue *offset = ir_emit_ptr_offset(proc, memory_offsets, index); - irValue *is_using = ir_emit_ptr_offset(proc, memory_usings, index); + irValue *is_using = ir_emit_ptr_offset(proc, memory_usings, index); ir_emit_store(proc, type_info, ir_type_info(proc, f->type)); if (f->token.string.len > 0) { @@ -10074,6 +10759,15 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info } ir_emit_store(proc, offset, ir_const_uintptr(foffset)); ir_emit_store(proc, is_using, ir_const_bool((f->flags&EntityFlag_Using) != 0)); + + if (t->Struct.tags.count > 0) { + String tag_string = t->Struct.tags[source_index]; + if (tag_string.len > 0) { + irValue *tag_ptr = ir_emit_ptr_offset(proc, memory_tags, index); + ir_emit_store(proc, tag_ptr, ir_const_string(tag_string)); + } + } + } irValue *cv = ir_const_int(count); @@ -10081,6 +10775,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 1), memory_names, cv); ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 2), memory_offsets, cv); ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 3), memory_usings, cv); + ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 4), memory_tags, cv); } break; } @@ -10482,12 +11177,14 @@ void ir_gen_tree(irGen *s) { // main :: proc(argc: i32, argv: ^^u8) -> i32 String name = str_lit("main"); +#if 0 if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) { // This is a bit hacky, // because this makes this function the first function run in the executable // so it won't actually have the argc/argv arguments. name = str_lit("ProgramEntry"); } +#endif Type *proc_params = alloc_type_tuple(); Type *proc_results = alloc_type_tuple(); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b008fa682..d47dfc898 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -323,12 +323,15 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) { void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) { + set_procedure_abi_types(heap_allocator(), t); + i64 word_bits = 8*build_context.word_size; t = base_type(t); GB_ASSERT(is_type_proc(t)); isize param_count = t->Proc.param_count; isize result_count = t->Proc.result_count; + ir_print_proc_results(f, m, t); ir_write_string(f, str_lit(" (")); if (t->Proc.return_by_pointer) { @@ -418,20 +421,23 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) { } return; - // case Basic_f16: ir_write_str_lit(f, "half"); return; - case Basic_f32: ir_write_str_lit(f, "float"); return; - case Basic_f64: ir_write_str_lit(f, "double"); return; + // case Basic_f16: ir_write_str_lit(f, "half"); return; + case Basic_f32: ir_write_str_lit(f, "float"); return; + case Basic_f64: ir_write_str_lit(f, "double"); return; + + // case Basic_complex32: ir_write_str_lit(f, "%%..complex32"); return; + case Basic_complex64: ir_write_str_lit(f, "%..complex64"); return; + case Basic_complex128: ir_write_str_lit(f, "%..complex128"); return; - // case Basic_complex32: ir_write_str_lit(f, "%%..complex32"); return; - case Basic_complex64: ir_write_str_lit(f, "%..complex64"); return; - case Basic_complex128: ir_write_str_lit(f, "%..complex128"); return; + case Basic_quaternion128: ir_write_str_lit(f, "%..quaternion128"); return; + case Basic_quaternion256: ir_write_str_lit(f, "%..quaternion256"); return; - case Basic_any: ir_write_str_lit(f, "%..any"); return; - case Basic_rawptr: ir_write_str_lit(f, "%..rawptr"); return; - case Basic_string: ir_write_str_lit(f, "%..string"); return; - case Basic_cstring: ir_write_str_lit(f, "i8*"); return; + case Basic_any: ir_write_str_lit(f, "%..any"); return; + case Basic_rawptr: ir_write_str_lit(f, "%..rawptr"); return; + case Basic_string: ir_write_str_lit(f, "%..string"); return; + case Basic_cstring: ir_write_str_lit(f, "i8*"); return; - case Basic_typeid: ir_write_str_lit(f, "%..typeid"); return; + case Basic_typeid: ir_write_str_lit(f, "%..typeid"); return; } break; @@ -767,15 +773,17 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * case ExactValue_Float: { GB_ASSERT_MSG(is_type_float(type), "%s", type_to_string(type)); type = core_type(type); - u64 u = bit_cast<u64>(value.value_float); + u64 u_64 = bit_cast<u64>(value.value_float); + u32 u_32 = bit_cast<u32>(cast(f32)value.value_float); + #if 0 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 - // 16 bit mantissa: 10 bits + // 64 bit mantissa: 52 bits ==> 52-52 == 0 + // 32 bit mantissa: 23 bits ==> 52-23 == 29 + // 16 bit mantissa: 10 bits ==> 52=10 == 42 // 29 == 52-23 u >>= 29; u <<= 29; @@ -792,9 +800,24 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_fprintf(f, "0x%016llx", u); break; } + #else + switch (type->Basic.kind) { + case Basic_f32: { + ir_fprintf(f, "bitcast (i32 %u to float)", u_32); + break; + } + case Basic_f64: + ir_fprintf(f, "0x%016llx", u_64); + break; + default: + ir_fprintf(f, "0x%016llx", u_64); + break; + } + #endif break; } case ExactValue_Complex: { + // xy/ri format type = core_type(type); GB_ASSERT_MSG(is_type_complex(type), "%s", type_to_string(type)); Type *ft = base_complex_elem_type(type); @@ -807,6 +830,26 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_byte(f, '}'); break; } + + case ExactValue_Quaternion: { + // xyzw/ijkr format + type = core_type(type); + GB_ASSERT_MSG(is_type_quaternion(type), "%s", type_to_string(type)); + Type *ft = base_complex_elem_type(type); + ir_write_byte(f, ' '); + ir_write_byte(f, '{'); + ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft); + ir_write_byte(f, '}'); + break; + } + case ExactValue_Pointer: if (value.value_pointer == 0) { if (is_type_typeid(type)) { @@ -836,22 +879,86 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_str_lit(f, "zeroinitializer"); break; } - GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); + if (cl->elems[0]->kind == Ast_FieldValue) { + // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand + ir_write_byte(f, '['); + for (i64 i = 0; i < type->Array.count; i++) { + if (i > 0) ir_write_str_lit(f, ", "); + + bool found = false; + + for (isize j = 0; j < elem_count; j++) { + Ast *elem = cl->elems[j]; + ast_node(fv, FieldValue, elem); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } + if (lo == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + for (i64 k = lo; k < hi; k++) { + if (k > lo) ir_write_str_lit(f, ", "); + + ir_print_compound_element(f, m, tav.value, elem_type); + } + + found = true; + i += (hi-lo-1); + break; + } + } else { + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + if (index == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + ir_print_compound_element(f, m, tav.value, elem_type); + found = true; + break; + } + } + } - ir_write_byte(f, '['); + if (!found) { + ir_print_type(f, m, elem_type); + ir_write_byte(f, ' '); + ir_write_str_lit(f, "zeroinitializer"); + } + } + ir_write_byte(f, ']'); + } else { + GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); - for (isize i = 0; i < elem_count; i++) { - if (i > 0) ir_write_str_lit(f, ", "); - TypeAndValue tav = cl->elems[i]->tav; - GB_ASSERT(tav.mode != Addressing_Invalid); - ir_print_compound_element(f, m, tav.value, elem_type); - } - for (isize i = elem_count; i < type->Array.count; i++) { - if (i >= elem_count) ir_write_str_lit(f, ", "); - ir_print_compound_element(f, m, empty_exact_value, elem_type); - } + ir_write_byte(f, '['); - ir_write_byte(f, ']'); + for (isize i = 0; i < elem_count; i++) { + if (i > 0) ir_write_str_lit(f, ", "); + TypeAndValue tav = cl->elems[i]->tav; + GB_ASSERT(tav.mode != Addressing_Invalid); + ir_print_compound_element(f, m, tav.value, elem_type); + } + for (isize i = elem_count; i < type->Array.count; i++) { + if (i >= elem_count) ir_write_str_lit(f, ", "); + ir_print_compound_element(f, m, empty_exact_value, elem_type); + } + + ir_write_byte(f, ']'); + } } else if (is_type_simd_vector(type)) { ast_node(cl, CompoundLit, value.value_compound); @@ -927,7 +1034,8 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * if (type->Struct.is_packed) ir_write_byte(f, '<'); ir_write_byte(f, '{'); if (type->Struct.custom_align > 0) { - ir_fprintf(f, "[0 x <%lld x i8>] zeroinitializer", cast(i64)type->Struct.custom_align); + ir_print_alignment_prefix_hack(f, cast(i64)type->Struct.custom_align); + ir_write_str_lit(f, " zeroinitializer"); if (value_count > 0) { ir_write_string(f, str_lit(", ")); } @@ -1089,7 +1197,11 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin break; } case irValue_Param: - ir_print_encoded_local(f, value->Param.entity->token.string); + if (value->Param.index >= 0) { + ir_fprintf(f, "%%_.%d", value->Param.index); + } else { + ir_print_encoded_local(f, value->Param.entity->token.string); + } break; case irValue_SourceCodeLocation: { irValue *file = value->SourceCodeLocation.file; @@ -1124,7 +1236,8 @@ void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConven switch (cc) { case ProcCC_Odin: ir_write_str_lit(f, ""); break; case ProcCC_Contextless: ir_write_str_lit(f, ""); break; - case ProcCC_CDecl: ir_write_str_lit(f, "ccc "); break; + // case ProcCC_CDecl: ir_write_str_lit(f, "ccc "); break; + case ProcCC_CDecl: ir_write_str_lit(f, ""); break; case ProcCC_StdCall: ir_write_str_lit(f, "cc 64 "); break; case ProcCC_FastCall: ir_write_str_lit(f, "cc 65 "); break; case ProcCC_None: ir_write_str_lit(f, ""); break; @@ -1134,8 +1247,8 @@ void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConven void ir_print_context_parameter_prefix(irFileBuffer *f, irModule *m) { ir_print_type(f, m, t_context_ptr); - ir_write_str_lit(f, " noalias nonnull nocapture inreg "); - // ir_write_str_lit(f, " noalias nonnull nocapture "); + // ir_write_str_lit(f, " noalias nonnull nocapture inreg "); + ir_write_str_lit(f, " noalias nonnull nocapture "); } void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { @@ -1186,6 +1299,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { ir_write_str_lit(f, ", "); ir_print_type(f, m, type); ir_fprintf(f, "* %%%d, align 1", instr->ZeroInit.address->index); + // ir_fprintf(f, "* %%%d", instr->ZeroInit.address->index); break; } @@ -1888,11 +2002,11 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { if (e->flags&EntityFlag_NoAlias) { ir_write_str_lit(f, " noalias"); } + if (e->flags&EntityFlag_ImplicitReference) { + ir_write_str_lit(f, " nonnull dereferenceable"); + } ir_write_byte(f, ' '); irValue *arg = call->args[i]; - if (is_type_boolean(t)) { - - } ir_print_value(f, m, arg, t); param_index++; } @@ -1907,24 +2021,43 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { param_index++; } } else { - GB_ASSERT(call->args.count == params->variables.count); + // GB_ASSERT(call->args.count == params->variables.count); + isize arg_index = 0; for_array(i, params->variables) { Entity *e = params->variables[i]; GB_ASSERT(e != nullptr); - if (e->kind != Entity_Variable) continue; + if (e->kind != Entity_Variable) { + arg_index++; + continue; + } if (param_index > 0) ir_write_str_lit(f, ", "); - irValue *arg = call->args[i]; Type *t = proc_type->Proc.abi_compat_params[i]; - - ir_print_type(f, m, t); - if (e->flags&EntityFlag_NoAlias) { - ir_write_str_lit(f, " noalias"); + if (is_type_tuple(t)) { + for_array(j, t->Tuple.variables) { + if (j > 0) ir_write_str_lit(f, ", "); + + irValue *arg = call->args[arg_index++]; + + ir_print_type(f, m, t->Tuple.variables[j]->type); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + ir_write_byte(f, ' '); + ir_print_value(f, m, arg, t); + param_index++; + } + } else { + irValue *arg = call->args[arg_index++]; + ir_print_type(f, m, t); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + ir_write_byte(f, ' '); + ir_print_value(f, m, arg, t); + param_index++; } - ir_write_byte(f, ' '); - ir_print_value(f, m, arg, t); - param_index++; } } } @@ -1995,6 +2128,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { + set_procedure_abi_types(heap_allocator(), proc->type); + if (proc->body == nullptr) { ir_write_str_lit(f, "declare "); // if (proc->tags & ProcTag_dll_import) { @@ -2043,7 +2178,8 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { if (param_count > 0) { TypeTuple *params = &proc_type->params->Tuple; - for (isize i = 0; i < param_count; i++) { + isize parameter_index = 0; + for (isize i = 0; i < param_count; i++, parameter_index++) { Entity *e = params->variables[i]; Type *original_type = e->type; Type *abi_type = proc_type->abi_compat_params[i]; @@ -2053,16 +2189,29 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { if (i+1 == params->variables.count && proc_type->c_vararg) { ir_write_str_lit(f, " ..."); } else { - ir_print_type(f, m, abi_type); - if (e->flags&EntityFlag_NoAlias) { - ir_write_str_lit(f, " noalias"); - } - if (proc->body != nullptr) { - if (e->token.string != "" && !is_blank_ident(e->token)) { - ir_write_byte(f, ' '); - ir_print_encoded_local(f, e->token.string); - } else { - ir_fprintf(f, " %%_.param_%td", i); + if (is_type_tuple(abi_type)) { + for_array(j, abi_type->Tuple.variables) { + if (j > 0) ir_write_string(f, str_lit(", ")); + + Type *tft = abi_type->Tuple.variables[j]->type; + ir_print_type(f, m, tft); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + + if (proc->body != nullptr) { + ir_fprintf(f, " %%_.%td", parameter_index+j); + } + } + parameter_index += abi_type->Tuple.variables.count-1; + param_index += abi_type->Tuple.variables.count-1; + } else { + ir_print_type(f, m, abi_type); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + if (proc->body != nullptr) { + ir_fprintf(f, " %%_.%td", parameter_index); } } } @@ -2176,6 +2325,22 @@ void ir_print_type_name(irFileBuffer *f, irModule *m, irValue *v) { ir_write_byte(f, '\n'); } +bool ir_print_global_type_allowed(Type *t) { + if (t == nullptr) { + return true; + } + t = core_type(t); + switch (t->kind) { + case Type_DynamicArray: + case Type_Map: + case Type_Union: + case Type_BitField: + return false; + } + + return true; +} + void print_llvm_ir(irGen *ir) { irModule *m = &ir->module; @@ -2184,9 +2349,11 @@ void print_llvm_ir(irGen *ir) { defer (ir_file_buffer_destroy(f)); i32 word_bits = cast(i32)(8*build_context.word_size); - if (build_context.ODIN_OS == "osx" || build_context.ODIN_OS == "macos") { + if (build_context.ODIN_OS == "darwin") { GB_ASSERT(word_bits == 64); - ir_write_str_lit(f, "target triple = \"x86_64-apple-macosx10.8\"\n\n"); + ir_write_str_lit(f, "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n"); + ir_write_str_lit(f, "target triple = \"x86_64-apple-macosx10.8\"\n"); + ir_write_str_lit(f, "\n"); } else if (build_context.ODIN_OS == "windows") { ir_fprintf(f, "target triple = \"x86%s-pc-windows-msvc\"\n\n", word_bits == 64 ? "_64" : ""); if (word_bits == 64 && build_context.metrics.arch == TargetArch_amd64) { @@ -2210,6 +2377,13 @@ void print_llvm_ir(irGen *ir) { ir_print_encoded_local(f, str_lit("..complex128")); ir_write_str_lit(f, " = type {double, double} ; Basic_complex128\n"); + ir_print_encoded_local(f, str_lit("..quaternion64")); + ir_write_str_lit(f, " = type {half, half, half, half} ; Basic_quaternion64\n"); + ir_print_encoded_local(f, str_lit("..quaternion128")); + ir_write_str_lit(f, " = type {float, float, float, float} ; Basic_quaternion128\n"); + ir_print_encoded_local(f, str_lit("..quaternion256")); + ir_write_str_lit(f, " = type {double, double, double, double} ; Basic_quaternion256\n"); + ir_print_encoded_local(f, str_lit("..typeid")); ir_write_str_lit(f, " = type "); ir_print_type(f, m, t_uintptr); @@ -2339,7 +2513,7 @@ void print_llvm_ir(irGen *ir) { ir_print_type(f, m, g->entity->type); ir_write_byte(f, ' '); if (!g->is_foreign) { - if (g->value != nullptr) { + if (g->value != nullptr && ir_print_global_type_allowed(g->entity->type)) { ir_print_value(f, m, g->value, g->entity->type); } else { ir_write_string(f, str_lit("zeroinitializer")); @@ -2383,11 +2557,13 @@ void print_llvm_ir(irGen *ir) { for_array(di_index, m->debug_info.entries) { irDebugInfo *di = m->debug_info.entries[di_index].value; + GB_ASSERT_MSG(di != nullptr, "Invalid irDebugInfo"); ir_fprintf(f, "!%d = ", di->id); - switch (di->kind) { case irDebugInfo_CompileUnit: { - irDebugInfo *file = *map_get(&m->debug_info, hash_pointer(di->CompileUnit.file)); + irDebugInfo **found = map_get(&m->debug_info, hash_pointer(di->CompileUnit.file)); + GB_ASSERT_MSG(found != nullptr, "Missing debug info for: %.*s\n", LIT(di->CompileUnit.file->fullpath)); + irDebugInfo *file = *found; ir_fprintf(f, "distinct !DICompileUnit(" "language: DW_LANG_C_plus_plus" // Is this good enough? diff --git a/src/main.cpp b/src/main.cpp index 05fa6c366..acb580ca2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,7 +75,7 @@ i32 system_exec_command_line_app(char *name, char *fmt, ...) { va_end(va); cmd = make_string(cast(u8 *)&cmd_line, cmd_len-1); - //printf("do: %s\n", cmd_line); + // printf("do: %s\n", cmd_line); exit_code = system(&cmd_line[0]); // pid_t pid = fork(); @@ -160,8 +160,9 @@ void usage(String argv0) { print_usage_line(0, "Usage:"); print_usage_line(1, "%.*s command [arguments]", LIT(argv0)); print_usage_line(0, "Commands:"); - print_usage_line(1, "build compile .odin file as executable"); - print_usage_line(1, "run compile and run .odin file"); + print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable."); + print_usage_line(1, " one must contain the program's entry point, all must be in the same package."); + print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); print_usage_line(1, "check parse and type check .odin file"); print_usage_line(1, "query parse, type check, and output a .json file containing information about the program"); print_usage_line(1, "docs generate documentation for a .odin file"); @@ -210,9 +211,8 @@ enum BuildFlagKind { BuildFlag_Collection, BuildFlag_Define, BuildFlag_BuildMode, + BuildFlag_Target, BuildFlag_Debug, - BuildFlag_CrossCompile, - BuildFlag_CrossLibDir, BuildFlag_NoBoundsCheck, BuildFlag_NoCRT, BuildFlag_UseLLD, @@ -298,9 +298,8 @@ bool parse_build_flags(Array<String> args) { add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String); + add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_CrossCompile, str_lit("cross-compile"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_CrossLibDir, str_lit("cross-lib-dir"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None); @@ -447,7 +446,7 @@ bool parse_build_flags(Array<String> args) { path = substring(path, 0, string_extension_position(path)); } #endif - build_context.out_filepath = path; + build_context.out_filepath = path_to_full_path(heap_allocator(), path); } else { gb_printf_err("Invalid -out path, got %.*s\n", LIT(path)); bad_flags = true; @@ -478,33 +477,6 @@ bool parse_build_flags(Array<String> args) { build_context.keep_temp_files = true; break; - case BuildFlag_CrossCompile: { - GB_ASSERT(value.kind == ExactValue_String); - cross_compile_target = value.value_string; - #if defined(GB_SYSTEM_UNIX) && defined(GB_ARCH_64_BIT) - if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) { - - } else - #endif - { - gb_printf_err("Unsupported cross compilation target '%.*s'\n", LIT(cross_compile_target)); - gb_printf_err("Currently supported targets: Essence (from 64-bit Unixes only)\n"); - bad_flags = true; - } - break; - } - - case BuildFlag_CrossLibDir: { - GB_ASSERT(value.kind == ExactValue_String); - if (cross_compile_lib_dir.len) { - gb_printf_err("Multiple cross compilation library directories\n"); - bad_flags = true; - } else { - cross_compile_lib_dir = concatenate_strings(heap_allocator(), str_lit("-L"), value.value_string); - } - break; - } - case BuildFlag_Collection: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; @@ -623,7 +595,25 @@ bool parse_build_flags(Array<String> args) { break; } + case BuildFlag_Target: { + String str = value.value_string; + bool found = false; + + for (int i = 0; i < sizeof(named_targets) / sizeof(named_targets[0]); i++) { + if (str_eq_ignore_case(str, named_targets[i].name)) { + found = true; + selected_target_metrics = named_targets + i; + break; + } + } + + if (!found) { + gb_printf_err("Unknown target '%.*s'\n", LIT(str)); + bad_flags = true; + } + break; + } case BuildFlag_BuildMode: { GB_ASSERT(value.kind == ExactValue_String); @@ -635,7 +625,7 @@ bool parse_build_flags(Array<String> args) { break; } - if (str == "dll") { + if (str == "dll" || str == "shared") { build_context.is_dll = true; } else if (str == "exe") { build_context.is_dll = false; @@ -889,8 +879,8 @@ i32 exec_llvm_opt(String output_base) { } i32 exec_llvm_llc(String output_base) { -#if defined(GB_SYSTEM_WINDOWS) // For more arguments: http://llvm.org/docs/CommandGuide/llc.html +#if defined(GB_SYSTEM_WINDOWS) return system_exec_command_line_app("llvm-llc", "\"%.*sbin\\llc\" \"%.*s.bc\" -filetype=obj -O%d " "-o \"%.*s.obj\" " @@ -903,21 +893,19 @@ i32 exec_llvm_llc(String output_base) { LIT(build_context.llc_flags)); #else // NOTE(zangent): Linux / Unix is unfinished and not tested very well. - // For more arguments: http://llvm.org/docs/CommandGuide/llc.html return system_exec_command_line_app("llc", "llc \"%.*s.bc\" -filetype=obj -relocation-model=pic -O%d " "%.*s " - "%s" - "", + "%s%.*s", LIT(output_base), build_context.optimization_level, LIT(build_context.llc_flags), - str_eq_ignore_case(cross_compile_target, str_lit("Essence")) ? "-mtriple=x86_64-pc-none-elf" : ""); + build_context.cross_compiling ? "-mtriple=" : "", + (int) (build_context.cross_compiling ? build_context.target_triplet.len : 0), + build_context.target_triplet.text); #endif } - - int main(int arg_count, char **arg_ptr) { if (arg_count < 2) { usage(make_string_c(arg_ptr[0])); @@ -1026,7 +1014,7 @@ int main(int arg_count, char **arg_ptr) { } - init_build_context(); + init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr); if (build_context.word_size == 4) { print_usage_line(0, "%s 32-bit is not yet supported", args[0]); return 1; @@ -1121,6 +1109,17 @@ int main(int arg_count, char **arg_ptr) { return exit_code; } + if (build_context.cross_compiling) { + if (0) { +#ifdef GB_SYSTEM_UNIX + } else if (selected_target_metrics->metrics == &target_essence_amd64) { + system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s", + LIT(output_base), LIT(output_base), LIT(build_context.link_flags)); +#endif + } else { + gb_printf_err("Don't know how to cross compile to selected target.\n"); + } + } else { #if defined(GB_SYSTEM_WINDOWS) timings_start_section(&timings, str_lit("msvc-link")); @@ -1219,7 +1218,7 @@ int main(int arg_count, char **arg_ptr) { remove_temp_files(output_base); if (run_output) { - system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string)); + return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string)); } #else timings_start_section(&timings, str_lit("ld-link")); @@ -1241,15 +1240,17 @@ int main(int arg_count, char **arg_ptr) { // This allows you to specify '-f' in a #foreign_system_library, // without having to implement any new syntax specifically for MacOS. #if defined(GB_SYSTEM_OSX) - if (lib.len > 2 && lib[0] == '-' && lib[1] == 'f') { + if (string_ends_with(lib, str_lit(".framework"))) { // framework thingie - lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", (int)(lib.len) - 2, lib.text + 2); + String lib_name = lib; + lib_name = remove_extension_from_path(lib_name); + lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); } else if (string_ends_with(lib, str_lit(".a"))) { // static libs, absolute full path relative to the file in which the lib was imported from lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); } else if (string_ends_with(lib, str_lit(".dylib"))) { - // dynamic lib, relative path to executable - lib_str = gb_string_append_fmt(lib_str, " -l:%s/%.*s ", cwd, LIT(lib)); + // dynamic lib + lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); } else { // dynamic or static system lib, just link regularly searching system library paths lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); @@ -1309,11 +1310,7 @@ int main(int arg_count, char **arg_ptr) { // It probably has to do with including the entire CRT, but // that's quite a complicated issue to solve while remaining distro-agnostic. // Clang can figure out linker flags for us, and that's good enough _for now_. - if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) { - linker = "x86_64-elf-gcc -T core/sys/essence_linker_userland64.ld -ffreestanding -nostdlib -lgcc -g -z max-page-size=0x1000 -Wno-unused-command-line-argument"; - } else { - linker = "clang -Wno-unused-command-line-argument"; - } + linker = "clang -Wno-unused-command-line-argument"; #endif exit_code = system_exec_command_line_app("ld-link", @@ -1321,7 +1318,6 @@ int main(int arg_count, char **arg_ptr) { " %s " " %.*s " " %s " - " %.*s " #if defined(GB_SYSTEM_OSX) // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit. // NOTE: If you change this (although this minimum is as low as you can go with Odin working) @@ -1332,11 +1328,9 @@ int main(int arg_count, char **arg_ptr) { #endif , linker, LIT(output_base), LIT(output_base), LIT(output_ext), lib_str, - str_eq_ignore_case(cross_compile_target, str_lit("Essence")) ? "-lfreetype -lglue" : "-lc -lm", + "-lc -lm", LIT(build_context.link_flags), - link_settings, - LIT(cross_compile_lib_dir) - ); + link_settings); if (exit_code != 0) { return exit_code; } @@ -1366,9 +1360,10 @@ int main(int arg_count, char **arg_ptr) { //NOTE(thebirk): This whole thing is a little leaky String complete_path = concatenate_strings(heap_allocator(), output_base, output_ext); complete_path = path_to_full_path(heap_allocator(), complete_path); - system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string)); + return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string)); } #endif + } return 0; } diff --git a/src/parser.cpp b/src/parser.cpp index a377b773c..10aa10119 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -51,6 +51,7 @@ Token ast_token(Ast *node) { case Ast_ReturnStmt: return node->ReturnStmt.token; case Ast_ForStmt: return node->ForStmt.token; case Ast_RangeStmt: return node->RangeStmt.token; + case Ast_InlineRangeStmt: return node->InlineRangeStmt.inline_token; case Ast_CaseClause: return node->CaseClause.token; case Ast_SwitchStmt: return node->SwitchStmt.token; case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token; @@ -143,6 +144,7 @@ Ast *clone_ast(Ast *node) { case Ast_ProcLit: n->ProcLit.type = clone_ast(n->ProcLit.type); n->ProcLit.body = clone_ast(n->ProcLit.body); + n->ProcLit.where_clauses = clone_ast_array(n->ProcLit.where_clauses); break; case Ast_CompoundLit: n->CompoundLit.type = clone_ast(n->CompoundLit.type); @@ -257,6 +259,12 @@ Ast *clone_ast(Ast *node) { n->RangeStmt.expr = clone_ast(n->RangeStmt.expr); n->RangeStmt.body = clone_ast(n->RangeStmt.body); break; + case Ast_InlineRangeStmt: + n->InlineRangeStmt.val0 = clone_ast(n->InlineRangeStmt.val0); + n->InlineRangeStmt.val1 = clone_ast(n->InlineRangeStmt.val1); + n->InlineRangeStmt.expr = clone_ast(n->InlineRangeStmt.expr); + n->InlineRangeStmt.body = clone_ast(n->InlineRangeStmt.body); + break; case Ast_CaseClause: n->CaseClause.list = clone_ast_array(n->CaseClause.list); n->CaseClause.stmts = clone_ast_array(n->CaseClause.stmts); @@ -341,10 +349,12 @@ Ast *clone_ast(Ast *node) { n->StructType.fields = clone_ast_array(n->StructType.fields); n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params); n->StructType.align = clone_ast(n->StructType.align); + n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses); break; case Ast_UnionType: n->UnionType.variants = clone_ast_array(n->UnionType.variants); n->UnionType.polymorphic_params = clone_ast(n->UnionType.polymorphic_params); + n->UnionType.where_clauses = clone_ast_array(n->UnionType.where_clauses); break; case Ast_EnumType: n->EnumType.base_type = clone_ast(n->EnumType.base_type); @@ -417,7 +427,7 @@ void syntax_error(Ast *node, char *fmt, ...) { bool ast_node_expect(Ast *node, AstKind kind) { if (node->kind != kind) { - error(node, "Expected %.*s, got %.*s", LIT(ast_strings[kind]), LIT(ast_strings[node->kind])); + syntax_error(node, "Expected %.*s, got %.*s", LIT(ast_strings[kind]), LIT(ast_strings[node->kind])); return false; } return true; @@ -578,6 +588,7 @@ Ast *ast_undef(AstFile *f, Token token) { Ast *ast_basic_lit(AstFile *f, Token basic_lit) { Ast *result = alloc_ast_node(f, Ast_BasicLit); result->BasicLit.token = basic_lit; + result->BasicLit.value = exact_value_from_basic_literal(basic_lit); return result; } @@ -605,11 +616,13 @@ Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<Ast return result; } -Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags) { +Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags, Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_ProcLit); result->ProcLit.type = type; result->ProcLit.body = body; result->ProcLit.tags = tags; + result->ProcLit.where_token = where_token; + result->ProcLit.where_clauses = where_clauses; return result; } @@ -748,6 +761,18 @@ Ast *ast_range_stmt(AstFile *f, Token token, Ast *val0, Ast *val1, Token in_toke return result; } +Ast *ast_inline_range_stmt(AstFile *f, Token inline_token, Token for_token, Ast *val0, Ast *val1, Token in_token, Ast *expr, Ast *body) { + Ast *result = alloc_ast_node(f, Ast_InlineRangeStmt); + result->InlineRangeStmt.inline_token = inline_token; + result->InlineRangeStmt.for_token = for_token; + result->InlineRangeStmt.val0 = val0; + result->InlineRangeStmt.val1 = val1; + result->InlineRangeStmt.in_token = in_token; + result->InlineRangeStmt.expr = expr; + result->InlineRangeStmt.body = body; + return result; +} + Ast *ast_switch_stmt(AstFile *f, Token token, Ast *init, Ast *tag, Ast *body) { Ast *result = alloc_ast_node(f, Ast_SwitchStmt); result->SwitchStmt.token = token; @@ -805,13 +830,14 @@ Ast *ast_bad_decl(AstFile *f, Token begin, Token end) { return result; } -Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags, - CommentGroup *docs, CommentGroup *comment) { +Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags, Token tag, + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_Field); result->Field.names = names; result->Field.type = type; result->Field.default_value = default_value; result->Field.flags = flags; + result->Field.tag = tag; result->Field.docs = docs; result->Field.comment = comment; return result; @@ -898,7 +924,8 @@ Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_count, Ast *polymorphic_params, bool is_packed, bool is_raw_union, - Ast *align) { + Ast *align, + Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); result->StructType.token = token; result->StructType.fields = fields; @@ -907,17 +934,22 @@ Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_c result->StructType.is_packed = is_packed; result->StructType.is_raw_union = is_raw_union; result->StructType.align = align; + result->StructType.where_token = where_token; + result->StructType.where_clauses = where_clauses; return result; } -Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil) { +Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil, + Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; result->UnionType.variants = variants; result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; result->UnionType.no_nil = no_nil; + result->UnionType.where_token = where_token; + result->UnionType.where_clauses = where_clauses; return result; } @@ -1118,6 +1150,17 @@ Token advance_token(AstFile *f) { return prev; } +bool peek_token_kind(AstFile *f, TokenKind kind) { + for (isize i = f->curr_token_index+1; i < f->tokens.count; i++) { + Token tok = f->tokens[i]; + if (kind != Token_Comment && tok.kind == Token_Comment) { + continue; + } + return tok.kind == kind; + } + return false; +} + Token expect_token(AstFile *f, TokenKind kind) { Token prev = f->curr_token; if (prev.kind != kind) { @@ -1302,7 +1345,8 @@ bool is_semicolon_optional_for_node(AstFile *f, Ast *s) { case Ast_UnionType: case Ast_EnumType: case Ast_BitFieldType: - return true; + // Require semicolon within a procedure body + return f->curr_proc == nullptr; case Ast_ProcLit: return true; @@ -1336,10 +1380,6 @@ void expect_semicolon(AstFile *f, Ast *s) { return; } - switch (f->curr_token.kind) { - case Token_EOF: - return; - } if (s != nullptr) { if (prev_token.pos.line != f->curr_token.pos.line) { @@ -1352,12 +1392,21 @@ void expect_semicolon(AstFile *f, Ast *s) { case Token_CloseParen: case Token_else: return; + case Token_EOF: + if (is_semicolon_optional_for_node(f, s)) { + return; + } + break; } } String node_string = ast_strings[s->kind]; syntax_error(prev_token, "Expected ';' after %.*s, got %.*s", - LIT(node_string), LIT(token_strings[prev_token.kind])); + LIT(node_string), LIT(token_strings[f->curr_token.kind])); } else { + switch (f->curr_token.kind) { + case Token_EOF: + return; + } syntax_error(prev_token, "Expected ';'"); } fix_advance_to_next_stmt(f); @@ -1461,8 +1510,11 @@ Ast *parse_value(AstFile *f) { if (f->curr_token.kind == Token_OpenBrace) { return parse_literal_value(f, nullptr); } - - Ast *value = parse_expr(f, false); + Ast *value; + bool prev_allow_range = f->allow_range; + f->allow_range = true; + value = parse_expr(f, false); + f->allow_range = prev_allow_range; return value; } @@ -1614,7 +1666,7 @@ void check_polymorphic_params_for_type(AstFile *f, Ast *polymorphic_params, Toke for_array(i, field->Field.names) { Ast *name = field->Field.names[i]; if (name->kind == Ast_PolyType) { - error(name, "Polymorphic names are not needed for %.*s parameters", LIT(token.string)); + syntax_error(name, "Polymorphic names are not needed for %.*s parameters", LIT(token.string)); return; // TODO(bill): Err multiple times or just the once? } } @@ -1687,7 +1739,8 @@ Ast *parse_operand(AstFile *f, bool lhs) { operand = ast_bad_expr(f, token, f->curr_token); } operand->stmt_state_flags |= StmtStateFlag_no_deferred; - } */ else if (name.string == "file") { return ast_basic_directive(f, token, name.string); + } */ else if (name.string == "file") { + return ast_basic_directive(f, token, name.string); } else if (name.string == "line") { return ast_basic_directive(f, token, name.string); } else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string); } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name.string); @@ -1796,15 +1849,41 @@ Ast *parse_operand(AstFile *f, bool lhs) { } Ast *type = parse_proc_type(f, token); + Token where_token = {}; + Array<Ast *> where_clauses = {}; + u64 tags = 0; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + parse_proc_tags(f, &tags); + if ((tags & ProcTag_require_results) != 0) { + syntax_error(f->curr_token, "#require_results has now been replaced as an attribute @(require_results) on the declaration"); + tags &= ~ProcTag_require_results; + } + GB_ASSERT(type->kind == Ast_ProcType); + type->ProcType.tags = tags; if (f->allow_type && f->expr_level < 0) { + if (tags != 0) { + syntax_error(token, "A procedure type cannot have suffix tags"); + } + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure types"); + } return type; } - u64 tags = type->ProcType.tags; - if (allow_token(f, Token_Undef)) { - return ast_proc_lit(f, type, nullptr, tags); + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure literals without a defined body (replaced with ---)"); + } + return ast_proc_lit(f, type, nullptr, tags, where_token, where_clauses); } else if (f->curr_token.kind == Token_OpenBrace) { Ast *curr_proc = f->curr_proc; Ast *body = nullptr; @@ -1812,7 +1891,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = parse_body(f); f->curr_proc = curr_proc; - return ast_proc_lit(f, type, body, tags); + return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } else if (allow_token(f, Token_do)) { Ast *curr_proc = f->curr_proc; Ast *body = nullptr; @@ -1820,11 +1899,14 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = convert_stmt_to_body(f, parse_stmt(f)); f->curr_proc = curr_proc; - return ast_proc_lit(f, type, body, tags); + return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } if (tags != 0) { - syntax_error(token, "A procedure type cannot have tags"); + syntax_error(token, "A procedure type cannot have suffix tags"); + } + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure types"); } return type; @@ -1847,10 +1929,6 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_typeid: { Token token = expect_token(f, Token_typeid); - // Ast *specialization = nullptr; - // if (allow_token(f, Token_Quo)) { - // specialization = parse_type(f); - // } return ast_typeid_type(f, token, nullptr); } break; @@ -1952,6 +2030,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(token, "'#raw_union' cannot also be '#packed'"); } + Token where_token = {}; + Array<Ast *> where_clauses = {}; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + Token open = expect_token_after(f, Token_OpenBrace, "struct"); isize name_count = 0; @@ -1964,7 +2054,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { decls = fields->FieldList.list; } - return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, align); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, align, where_token, where_clauses); } break; case Token_union: { @@ -2005,6 +2095,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { } } + Token where_token = {}; + Array<Ast *> where_clauses = {}; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + Token open = expect_token_after(f, Token_OpenBrace, "union"); while (f->curr_token.kind != Token_CloseBrace && @@ -2020,7 +2122,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token close = expect_token(f, Token_CloseBrace); - return ast_union_type(f, token, variants, polymorphic_params, align, no_nil); + return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, where_token, where_clauses); } break; case Token_enum: { @@ -2091,7 +2193,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { bool prev_allow_range = f->allow_range; f->allow_range = true; - elem = parse_expr(f, false); + elem = parse_expr(f, true); f->allow_range = prev_allow_range; if (allow_token(f, Token_Semicolon)) { underlying = parse_type(f); @@ -2383,17 +2485,18 @@ i32 token_precedence(AstFile *f, TokenKind t) { case Token_LtEq: case Token_GtEq: return 5; + case Token_in: case Token_notin: - if (f->expr_level >= 0 || f->allow_in_expr) { - return 6; + if (f->expr_level < 0 && !f->allow_in_expr) { + return 0; } - return 0; + /*fallthrough*/ case Token_Add: case Token_Sub: case Token_Or: case Token_Xor: - return 7; + return 6; case Token_Mul: case Token_Quo: case Token_Mod: @@ -2402,7 +2505,7 @@ i32 token_precedence(AstFile *f, TokenKind t) { case Token_AndNot: case Token_Shl: case Token_Shr: - return 8; + return 7; } return 0; } @@ -2649,7 +2752,7 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) { allow_token(f, Token_in); bool prev_allow_range = f->allow_range; f->allow_range = true; - Ast *expr = parse_expr(f, false); + Ast *expr = parse_expr(f, true); f->allow_range = prev_allow_range; auto rhs = array_make<Ast *>(heap_allocator(), 0, 1); @@ -2740,7 +2843,8 @@ Ast *parse_results(AstFile *f, bool *diverging) { Array<Ast *> empty_names = {}; auto list = array_make<Ast *>(heap_allocator(), 0, 1); Ast *type = parse_type(f); - array_add(&list, ast_field(f, empty_names, type, nullptr, 0, nullptr, nullptr)); + Token tag = {}; + array_add(&list, ast_field(f, empty_names, type, nullptr, 0, tag, nullptr, nullptr)); return ast_field_list(f, begin_token, list); } @@ -2795,8 +2899,6 @@ Ast *parse_proc_type(AstFile *f, Token proc_token) { results = parse_results(f, &diverging); u64 tags = 0; - parse_proc_tags(f, &tags); - bool is_generic = false; for_array(i, params->FieldList.list) { @@ -3095,6 +3197,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi Ast *type = nullptr; Ast *default_value = nullptr; + Token tag = {}; expect_token_after(f, Token_Colon, "field list"); if (f->curr_token.kind != Token_Eq) { @@ -3132,16 +3235,25 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value"); } + if (type != nullptr && default_value == nullptr) { + if (f->curr_token.kind == Token_String) { + tag = expect_token(f, Token_String); + if ((allowed_flags & FieldFlag_Tags) == 0) { + syntax_error(tag, "Field tags are only allowed within structures"); + } + } + } + parse_expect_field_separator(f, type); - Ast *param = ast_field(f, names, type, default_value, set_flags, docs, f->line_comment); + Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); while (f->curr_token.kind != follow && f->curr_token.kind != Token_EOF) { CommentGroup *docs = f->lead_comment; - u32 set_flags = parse_field_prefixes(f); + Token tag = {}; Array<Ast *> names = parse_ident_list(f, allow_poly_names); if (names.count == 0) { syntax_error(f->curr_token, "Empty field declaration"); @@ -3184,9 +3296,18 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value"); } + if (type != nullptr && default_value == nullptr) { + if (f->curr_token.kind == Token_String) { + tag = expect_token(f, Token_String); + if ((allowed_flags & FieldFlag_Tags) == 0) { + syntax_error(tag, "Field tags are only allowed within structures"); + } + } + } + bool ok = parse_expect_field_separator(f, param); - Ast *param = ast_field(f, names, type, default_value, set_flags, docs, f->line_comment); + Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); if (!ok) { @@ -3210,8 +3331,8 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi token.pos = ast_token(type).pos; names[0] = ast_ident(f, token); u32 flags = check_field_prefixes(f, list.count, allowed_flags, list[i].flags); - - Ast *param = ast_field(f, names, list[i].node, nullptr, flags, docs, f->line_comment); + Token tag = {}; + Ast *param = ast_field(f, names, list[i].node, nullptr, flags, tag, docs, f->line_comment); array_add(¶ms, param); } @@ -3758,9 +3879,55 @@ Ast *parse_stmt(AstFile *f) { Token token = f->curr_token; switch (token.kind) { // Operands - case Token_context: // Also allows for `context =` case Token_inline: + if (peek_token_kind(f, Token_for)) { + Token inline_token = expect_token(f, Token_inline); + Token for_token = expect_token(f, Token_for); + Ast *val0 = nullptr; + Ast *val1 = nullptr; + Token in_token = {}; + Ast *expr = nullptr; + Ast *body = nullptr; + + bool bad_stmt = false; + + if (f->curr_token.kind != Token_in) { + Array<Ast *> idents = parse_ident_list(f, false); + switch (idents.count) { + case 1: + val0 = idents[0]; + break; + case 2: + val0 = idents[0]; + val1 = idents[1]; + break; + default: + syntax_error(for_token, "Expected either 1 or 2 identifiers"); + bad_stmt = true; + break; + } + } + in_token = expect_token(f, Token_in); + + bool prev_allow_range = f->allow_range; + f->allow_range = true; + expr = parse_expr(f, true); + f->allow_range = prev_allow_range; + + if (allow_token(f, Token_do)) { + body = convert_stmt_to_body(f, parse_stmt(f)); + } else { + body = parse_block_stmt(f, false); + } + if (bad_stmt) { + return ast_bad_stmt(f, inline_token, f->curr_token); + } + return ast_inline_range_stmt(f, inline_token, for_token, val0, val1, in_token, expr, body); + } + /* fallthrough */ case Token_no_inline: + case Token_context: // Also allows for `context =` + case Token_proc: case Token_Ident: case Token_Integer: case Token_Float: @@ -3808,34 +3975,6 @@ Ast *parse_stmt(AstFile *f) { return s; } - // case Token_static: { - // CommentGroup *docs = f->lead_comment; - // Token token = expect_token(f, Token_static); - - // Ast *decl = nullptr; - // Array<Ast *> list = parse_lhs_expr_list(f); - // if (list.count == 0) { - // syntax_error(token, "Illegal use of 'static' statement"); - // expect_semicolon(f, nullptr); - // return ast_bad_stmt(f, token, f->curr_token); - // } - - // expect_token_after(f, Token_Colon, "identifier list"); - // decl = parse_value_decl(f, list, docs); - - // if (decl != nullptr && decl->kind == Ast_ValueDecl) { - // if (decl->ValueDecl.is_mutable) { - // decl->ValueDecl.is_static = true; - // } else { - // error(token, "'static' may only be currently used with variable declaration"); - // } - // return decl; - // } - - // syntax_error(token, "Illegal use of 'static' statement"); - // return ast_bad_stmt(f, token, f->curr_token); - // } break; - case Token_using: { CommentGroup *docs = f->lead_comment; Token token = expect_token(f, Token_using); @@ -3909,12 +4048,10 @@ Ast *parse_stmt(AstFile *f) { } else if (tag == "assert") { Ast *t = ast_basic_directive(f, hash_token, tag); return ast_expr_stmt(f, parse_call_expr(f, t)); - } /* else if (name.string == "no_deferred") { - s = parse_stmt(f); - s->stmt_state_flags |= StmtStateFlag_no_deferred; - } */ - - if (tag == "include") { + } else if (tag == "panic") { + Ast *t = ast_basic_directive(f, hash_token, tag); + return ast_expr_stmt(f, parse_call_expr(f, t)); + } else if (tag == "include") { syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?"); s = ast_bad_stmt(f, token, f->curr_token); } else { @@ -4037,7 +4174,6 @@ bool init_parser(Parser *p) { map_init(&p->package_map, heap_allocator()); array_init(&p->packages, heap_allocator()); array_init(&p->package_imports, heap_allocator()); - array_init(&p->files_to_process, heap_allocator()); gb_mutex_init(&p->file_add_mutex); gb_mutex_init(&p->file_decl_mutex); return true; @@ -4060,7 +4196,6 @@ void destroy_parser(Parser *p) { #endif array_free(&p->packages); array_free(&p->package_imports); - array_free(&p->files_to_process); string_set_destroy(&p->imported_files); map_destroy(&p->package_map); gb_mutex_destroy(&p->file_add_mutex); @@ -4077,19 +4212,32 @@ void parser_add_package(Parser *p, AstPackage *pkg) { if (found) { GB_ASSERT(pkg->files.count > 0); AstFile *f = pkg->files[0]; - error(f->package_token, "Non-unique package name '%.*s'", LIT(pkg->name)); + syntax_error(f->package_token, "Non-unique package name '%.*s'", LIT(pkg->name)); GB_ASSERT((*found)->files.count > 0); TokenPos pos = (*found)->files[0]->package_token.pos; - gb_printf_err("\tpreviously declared at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column); + error_line("\tpreviously declared at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column); } else { map_set(&p->package_map, key, pkg); } } } +ParseFileError process_imported_file(Parser *p, ImportedFile const &imported_file); + +WORKER_TASK_PROC(parser_worker_proc) { + ParserWorkerData *wd = cast(ParserWorkerData *)data; + ParseFileError err = process_imported_file(wd->parser, wd->imported_file); + return cast(isize)err; +} + + void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) { - ImportedFile f = {pkg, fi, pos, p->files_to_process.count}; - array_add(&p->files_to_process, f); + // TODO(bill): Use a better allocator + ImportedFile f = {pkg, fi, pos, p->file_to_process_count++}; + auto wd = gb_alloc_item(heap_allocator(), ParserWorkerData); + wd->parser = p; + wd->imported_file = f; + thread_pool_add_task(&parser_thread_pool, parser_worker_proc, wd); } @@ -4140,22 +4288,22 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path, switch (rd_err) { case ReadDirectory_InvalidPath: - error(pos, "Invalid path: %.*s", LIT(rel_path)); + syntax_error(pos, "Invalid path: %.*s", LIT(rel_path)); return false; case ReadDirectory_NotExists: - error(pos, "Path does not exist: %.*s", LIT(rel_path)); + syntax_error(pos, "Path does not exist: %.*s", LIT(rel_path)); return false; case ReadDirectory_Permission: - error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); + syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); return false; case ReadDirectory_NotDir: - error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path)); + syntax_error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path)); return false; case ReadDirectory_Empty: - error(pos, "Empty directory: %.*s", LIT(rel_path)); + syntax_error(pos, "Empty directory: %.*s", LIT(rel_path)); return false; case ReadDirectory_Unknown: - error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); + syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); return false; } @@ -4257,7 +4405,7 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, String file_str = {}; if (colon_pos == 0) { - error(node, "Expected a collection name"); + syntax_error(node, "Expected a collection name"); return false; } @@ -4272,32 +4420,19 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, if (has_windows_drive) { String sub_file_path = substring(file_str, 3, file_str.len); if (!is_import_path_valid(sub_file_path)) { - error(node, "Invalid import path: '%.*s'", LIT(file_str)); + syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str)); return false; } } else if (!is_import_path_valid(file_str)) { - error(node, "Invalid import path: '%.*s'", LIT(file_str)); + syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str)); return false; } - if (is_package_name_reserved(file_str)) { - *path = file_str; - return true; - } - - if (file_mutex) gb_mutex_lock(file_mutex); - defer (if (file_mutex) gb_mutex_unlock(file_mutex)); - - - if (node->kind == Ast_ForeignImportDecl) { - node->ForeignImportDecl.collection_name = collection_name; - } - if (collection_name.len > 0) { if (collection_name == "system") { if (node->kind != Ast_ForeignImportDecl) { - error(node, "The library collection 'system' is restrict for 'foreign_library'"); + syntax_error(node, "The library collection 'system' is restrict for 'foreign_library'"); return false; } else { *path = file_str; @@ -4305,7 +4440,7 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, } } else if (!find_library_collection_path(collection_name, &base_dir)) { // NOTE(bill): It's a naughty name - error(node, "Unknown library collection: '%.*s'", LIT(collection_name)); + syntax_error(node, "Unknown library collection: '%.*s'", LIT(collection_name)); return false; } } else { @@ -4324,6 +4459,20 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, #endif } + + if (is_package_name_reserved(file_str)) { + *path = file_str; + return true; + } + + if (file_mutex) gb_mutex_lock(file_mutex); + defer (if (file_mutex) gb_mutex_unlock(file_mutex)); + + + if (node->kind == Ast_ForeignImportDecl) { + node->ForeignImportDecl.collection_name = collection_name; + } + if (has_windows_drive) { *path = file_str; } else { @@ -4412,7 +4561,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *> array_add(&fl->fullpaths, fullpath); } if (fl->fullpaths.count == 0) { - error(decls[i], "No foreign paths found"); + syntax_error(decls[i], "No foreign paths found"); decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]); goto end; } @@ -4461,7 +4610,7 @@ bool parse_build_tag(Token token_for_pos, String s) { is_notted = true; p = substring(p, 1, p.len); if (p.len == 0) { - error(token_for_pos, "Expected a build platform after '!'"); + syntax_error(token_for_pos, "Expected a build platform after '!'"); break; } } @@ -4490,7 +4639,7 @@ bool parse_build_tag(Token token_for_pos, String s) { } } if (os == TargetOs_Invalid && arch == TargetArch_Invalid) { - error(token_for_pos, "Invalid build tag platform: %.*s", LIT(p)); + syntax_error(token_for_pos, "Invalid build tag platform: %.*s", LIT(p)); break; } } @@ -4536,11 +4685,11 @@ bool parse_file(Parser *p, AstFile *f) { Token package_name = expect_token_after(f, Token_Ident, "package"); if (package_name.kind == Token_Ident) { if (package_name.string == "_") { - error(package_name, "Invalid package name '_'"); + syntax_error(package_name, "Invalid package name '_'"); } else if (f->pkg->kind != Package_Runtime && package_name.string == "runtime") { - error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); + syntax_error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); } else if (is_package_name_reserved(package_name.string)) { - error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); + syntax_error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); } } f->package_name = package_name.string; @@ -4590,9 +4739,9 @@ bool parse_file(Parser *p, AstFile *f) { } -ParseFileError process_imported_file(Parser *p, ImportedFile imported_file) { +ParseFileError process_imported_file(Parser *p, ImportedFile const &imported_file) { AstPackage *pkg = imported_file.pkg; - FileInfo *fi = &imported_file.fi; + FileInfo const *fi = &imported_file.fi; TokenPos pos = imported_file.pos; AstFile *file = gb_alloc_item(heap_allocator(), AstFile); @@ -4607,38 +4756,35 @@ ParseFileError process_imported_file(Parser *p, ImportedFile imported_file) { if (err != ParseFile_None) { if (err == ParseFile_EmptyFile) { if (fi->fullpath == p->init_fullpath) { - error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath)); + syntax_error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath)); gb_exit(1); } - goto skip; - } + } else { + switch (err) { + case ParseFile_WrongExtension: + syntax_error(pos, "Failed to parse file: %.*s; invalid file extension: File must have the extension '.odin'", LIT(fi->name)); + break; + case ParseFile_InvalidFile: + syntax_error(pos, "Failed to parse file: %.*s; invalid file or cannot be found", LIT(fi->name)); + break; + case ParseFile_Permission: + syntax_error(pos, "Failed to parse file: %.*s; file permissions problem", LIT(fi->name)); + break; + case ParseFile_NotFound: + syntax_error(pos, "Failed to parse file: %.*s; file cannot be found ('%.*s')", LIT(fi->name), LIT(fi->fullpath)); + break; + case ParseFile_InvalidToken: + syntax_error(err_pos, "Failed to parse file: %.*s; invalid token found in file", LIT(fi->name)); + break; + case ParseFile_EmptyFile: + syntax_error(pos, "Failed to parse file: %.*s; file contains no tokens", LIT(fi->name)); + break; + } - switch (err) { - case ParseFile_WrongExtension: - error(pos, "Failed to parse file: %.*s; invalid file extension: File must have the extension '.odin'", LIT(fi->name)); - break; - case ParseFile_InvalidFile: - error(pos, "Failed to parse file: %.*s; invalid file or cannot be found", LIT(fi->name)); - break; - case ParseFile_Permission: - error(pos, "Failed to parse file: %.*s; file permissions problem", LIT(fi->name)); - break; - case ParseFile_NotFound: - error(pos, "Failed to parse file: %.*s; file cannot be found ('%.*s')", LIT(fi->name), LIT(fi->fullpath)); - break; - case ParseFile_InvalidToken: - error(err_pos, "Failed to parse file: %.*s; invalid token found in file", LIT(fi->name)); - break; - case ParseFile_EmptyFile: - error(pos, "Failed to parse file: %.*s; file contains no tokens", LIT(fi->name)); - break; + return err; } - - return err; } - -skip: if (parse_file(p, file)) { gb_mutex_lock(&p->file_add_mutex); defer (gb_mutex_unlock(&p->file_add_mutex)); @@ -4648,42 +4794,33 @@ skip: if (pkg->name.len == 0) { pkg->name = file->package_name; } else if (file->tokens.count > 0 && pkg->name != file->package_name) { - error(file->package_token, "Different package name, expected '%.*s', got '%.*s'", LIT(pkg->name), LIT(file->package_name)); + syntax_error(file->package_token, "Different package name, expected '%.*s', got '%.*s'", LIT(pkg->name), LIT(file->package_name)); } p->total_line_count += file->tokenizer.line_count; p->total_token_count += file->tokens.count; } + return ParseFile_None; } -GB_THREAD_PROC(parse_worker_file_proc) { - if (thread == nullptr) return 0; - auto *p = cast(Parser *)thread->user_data; - isize index = thread->user_index; - gb_mutex_lock(&p->file_add_mutex); - auto file_to_process = p->files_to_process[index]; - gb_mutex_unlock(&p->file_add_mutex); - ParseFileError err = process_imported_file(p, file_to_process); - return cast(isize)err; -} - ParseFileError parse_packages(Parser *p, String init_filename) { GB_ASSERT(init_filename.text[init_filename.len] == 0); - // char *fullpath_str = gb_path_get_full_name(heap_allocator(), cast(char const *)&init_filename[0]); - // String init_fullpath = string_trim_whitespace(make_string_c(fullpath_str)); + isize thread_count = gb_max(build_context.thread_count, 1); + isize worker_count = thread_count-1; // NOTE(bill): The main thread will also be used for work + thread_pool_init(&parser_thread_pool, heap_allocator(), worker_count, "ParserWork"); + String init_fullpath = path_to_full_path(heap_allocator(), init_filename); if (!path_is_directory(init_fullpath)) { String const ext = str_lit(".odin"); if (!string_ends_with(init_fullpath, ext)) { - gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); + error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); return ParseFile_WrongExtension; } } - TokenPos init_pos = {}; if (!build_context.generate_docs) { String s = get_fullpath_core(heap_allocator(), str_lit("runtime")); @@ -4693,76 +4830,17 @@ ParseFileError parse_packages(Parser *p, String init_filename) { try_add_import_path(p, init_fullpath, init_fullpath, init_pos, Package_Init); p->init_fullpath = init_fullpath; -#if 1 - isize thread_count = gb_max(build_context.thread_count, 1); - if (thread_count > 1) { - isize volatile curr_import_index = 0; - isize initial_file_count = p->files_to_process.count; - // NOTE(bill): Make sure that these are in parsed in this order - for (isize i = 0; i < initial_file_count; i++) { - ParseFileError err = process_imported_file(p, p->files_to_process[i]); - if (err != ParseFile_None) { - return err; - } - curr_import_index++; - } - - auto worker_threads = array_make<gbThread>(heap_allocator(), thread_count); - defer (array_free(&worker_threads)); - - for_array(i, worker_threads) { - gbThread *t = &worker_threads[i]; - gb_thread_init(t); - } - defer (for_array(i, worker_threads) { - gb_thread_destroy(&worker_threads[i]); - }); + thread_pool_start(&parser_thread_pool); + thread_pool_wait_to_process(&parser_thread_pool); - auto errors = array_make<ParseFileError>(heap_allocator(), 0, 16); - - for (;;) { - bool are_any_alive = false; - for_array(i, worker_threads) { - gbThread *t = &worker_threads[i]; - if (gb_thread_is_running(t)) { - are_any_alive = true; - } else if (curr_import_index < p->files_to_process.count) { - auto curr_err = cast(ParseFileError)t->return_value; - if (curr_err != ParseFile_None) { - array_add(&errors, curr_err); - } else { - t->user_index = curr_import_index; - curr_import_index++; - gb_thread_start(t, parse_worker_file_proc, p); - are_any_alive = true; - } - } - } - if (!are_any_alive && curr_import_index >= p->files_to_process.count) { - break; - } - } - - if (errors.count > 0) { - return errors[errors.count-1]; - } - } else { - for_array(i, p->files_to_process) { - ParseFileError err = process_imported_file(p, p->files_to_process[i]); - if (err != ParseFile_None) { - return err; - } - } - } -#else - for_array(i, p->files_to_process) { - ImportedFile f = p->files_to_process[i]; - ParseFileError err = process_imported_file(p, f); + // NOTE(bill): Get the last error and use that + for (isize i = parser_thread_pool.task_tail-1; i >= 0; i--) { + WorkerTask *task = &parser_thread_pool.tasks[i]; + ParseFileError err = cast(ParseFileError)task->result; if (err != ParseFile_None) { return err; } } -#endif return ParseFile_None; } diff --git a/src/parser.hpp b/src/parser.hpp index 9f4f68a7b..f07f3ce0d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -134,13 +134,26 @@ struct Parser { Map<AstPackage *> package_map; // Key: String (package name) Array<AstPackage *> packages; Array<ImportedPackage> package_imports; - Array<ImportedFile> files_to_process; + isize file_to_process_count; isize total_token_count; isize total_line_count; gbMutex file_add_mutex; gbMutex file_decl_mutex; }; + +gb_global ThreadPool parser_thread_pool = {}; + +struct ParserWorkerData { + Parser *parser; + ImportedFile imported_file; +}; + + + + + + enum ProcInlining { ProcInlining_none = 0, ProcInlining_inline = 1, @@ -186,11 +199,12 @@ enum FieldFlag { FieldFlag_c_vararg = 1<<3, FieldFlag_auto_cast = 1<<4, + FieldFlag_Tags = 1<<10, FieldFlag_Results = 1<<16, FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast, - FieldFlag_Struct = FieldFlag_using, + FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags, }; enum StmtAllowFlag { @@ -208,6 +222,7 @@ enum StmtAllowFlag { AST_KIND(Undef, "undef", Token) \ AST_KIND(BasicLit, "basic literal", struct { \ Token token; \ + ExactValue value; \ }) \ AST_KIND(BasicDirective, "basic directive", struct { \ Token token; \ @@ -228,11 +243,14 @@ enum StmtAllowFlag { Ast *body; \ u64 tags; \ ProcInlining inlining; \ + Token where_token; \ + Array<Ast *> where_clauses; \ }) \ AST_KIND(CompoundLit, "compound literal", struct { \ Ast *type; \ Array<Ast *> elems; \ Token open, close; \ + i64 max_count; \ }) \ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ @@ -325,6 +343,15 @@ AST_KIND(_ComplexStmtBegin, "", bool) \ Ast *expr; \ Ast *body; \ }) \ + AST_KIND(InlineRangeStmt, "inline range statement", struct { \ + Token inline_token; \ + Token for_token; \ + Ast *val0; \ + Ast *val1; \ + Token in_token; \ + Ast *expr; \ + Ast *body; \ + }) \ AST_KIND(CaseClause, "case clause", struct { \ Token token; \ Array<Ast *> list; \ @@ -412,6 +439,7 @@ AST_KIND(_DeclEnd, "", bool) \ Array<Ast *> names; \ Ast * type; \ Ast * default_value; \ + Token tag; \ u32 flags; \ CommentGroup * docs; \ CommentGroup * comment; \ @@ -465,20 +493,24 @@ AST_KIND(_TypeBegin, "", bool) \ Ast *elem; \ }) \ AST_KIND(StructType, "struct type", struct { \ - Token token; \ - Array<Ast *> fields; \ - isize field_count; \ - Ast *polymorphic_params; \ - Ast *align; \ - bool is_packed; \ - bool is_raw_union; \ + Token token; \ + Array<Ast *> fields; \ + isize field_count; \ + Ast *polymorphic_params; \ + Ast *align; \ + Token where_token; \ + Array<Ast *> where_clauses; \ + bool is_packed; \ + bool is_raw_union; \ }) \ AST_KIND(UnionType, "union type", struct { \ - Token token; \ - Array<Ast *> variants; \ - Ast *polymorphic_params; \ - Ast * align; \ - bool no_nil; \ + Token token; \ + Array<Ast *> variants; \ + Ast *polymorphic_params; \ + Ast * align; \ + bool no_nil; \ + Token where_token; \ + Array<Ast *> where_clauses; \ }) \ AST_KIND(EnumType, "enum type", struct { \ Token token; \ diff --git a/src/priority_queue.cpp b/src/priority_queue.cpp index 7c36e6a22..aee2061b5 100644 --- a/src/priority_queue.cpp +++ b/src/priority_queue.cpp @@ -20,7 +20,7 @@ bool priority_queue_shift_down(PriorityQueue<T> *pq, isize i0, isize n) { if (j2 < n && pq->cmp(&pq->queue[0], j2, j1) < 0) { j = j2; } - if (pq->cmp(&pq->queue[0], i, j) < 0) break; + if (pq->cmp(&pq->queue[0], j, i) >= 0) break; pq->swap(&pq->queue[0], i, j); i = j; @@ -32,7 +32,7 @@ template <typename T> void priority_queue_shift_up(PriorityQueue<T> *pq, isize j) { while (0 <= j && j < pq->queue.count) { isize i = (j-1)/2; - if (i == j || pq->cmp(&pq->queue[0], i, j) < 0) { + if (i == j || pq->cmp(&pq->queue[0], j, i) >= 0) { break; } pq->swap(&pq->queue[0], i, j); diff --git a/src/range_cache.cpp b/src/range_cache.cpp new file mode 100644 index 000000000..9701fb432 --- /dev/null +++ b/src/range_cache.cpp @@ -0,0 +1,70 @@ + +// Integers only +struct RangeValue { + i64 lo; + i64 hi; +}; + +struct RangeCache { + Array<RangeValue> ranges; +}; + + +RangeCache range_cache_make(gbAllocator a) { + RangeCache cache = {}; + array_init(&cache.ranges, a); + return cache; +} + +void range_cache_destroy(RangeCache *c) { + array_free(&c->ranges); +} + +bool range_cache_add_index(RangeCache *c, i64 index) { + for_array(i, c->ranges) { + RangeValue v = c->ranges[i]; + if (v.lo <= index && index <= v.hi) { + return false; + } + } + RangeValue v = {index, index}; + array_add(&c->ranges, v); + return true; +} + + +bool range_cache_add_range(RangeCache *c, i64 lo, i64 hi) { + GB_ASSERT(lo <= hi); + for_array(i, c->ranges) { + RangeValue v = c->ranges[i]; + if (hi < v.lo) { + continue; + } + if (lo > v.hi) { + continue; + } + + if (v.hi < hi) { + v.hi = hi; + } + if (lo < v.lo) { + v.lo = lo; + } + c->ranges[i] = v; + return false; + } + RangeValue v = {lo, hi}; + array_add(&c->ranges, v); + return true; +} + + +bool range_cache_index_exists(RangeCache *c, i64 index) { + for_array(i, c->ranges) { + RangeValue v = c->ranges[i]; + if (v.lo <= index && index <= v.hi) { + return true; + } + } + return false; +} diff --git a/src/string.cpp b/src/string.cpp index 774061edf..6812c5c39 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -404,7 +404,7 @@ String16 string_to_string16(gbAllocator a, String s) { } text[len] = 0; - return make_string16(text, len-1); + return make_string16(text, len); } @@ -440,12 +440,94 @@ String string16_to_string(gbAllocator a, String16 s) { +bool is_printable(Rune r) { + if (r <= 0xff) { + if (0x20 <= r && r <= 0x7e) { + return true; + } + if (0xa1 <= r && r <= 0xff) { + return r != 0xad; + } + return false; + } + return false; +} +gb_global char const lower_hex[] = "0123456789abcdef"; + +String quote_to_ascii(gbAllocator a, String str, u8 quote='"') { + u8 *s = str.text; + isize n = str.len; + auto buf = array_make<u8>(a, 0, n); + array_add(&buf, quote); + for (isize width = 0; n > 0; s += width, n -= width) { + Rune r = cast(Rune)s[0]; + width = 1; + if (r >= 0x80) { + width = gb_utf8_decode(s, n, &r); + } + if (width == 1 && r == GB_RUNE_INVALID) { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'x'); + array_add(&buf, cast(u8)lower_hex[s[0]>>4]); + array_add(&buf, cast(u8)lower_hex[s[0]&0xf]); + continue; + } + if (r == quote || r == '\\') { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, u8(r)); + continue; + } + if (r < 0x80 && is_printable(r)) { + array_add(&buf, u8(r)); + continue; + } + switch (r) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + default: + if (r < ' ') { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'x'); + array_add(&buf, cast(u8)lower_hex[b>>4]); + array_add(&buf, cast(u8)lower_hex[b&0xf]); + } + if (r > GB_RUNE_MAX) { + r = 0XFFFD; + } + if (r < 0x10000) { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'u'); + for (isize i = 12; i >= 0; i -= 4) { + array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]); + } + } else { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'U'); + for (isize i = 28; i >= 0; i -= 4) { + array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]); + } + } + } + } - + array_add(&buf, quote); + String res = {}; + res.text = buf.data; + res.len = buf.count; + return res; +} diff --git a/src/thread_pool.cpp b/src/thread_pool.cpp new file mode 100644 index 000000000..2467ba609 --- /dev/null +++ b/src/thread_pool.cpp @@ -0,0 +1,184 @@ +// worker_queue.cpp + +#define WORKER_TASK_PROC(name) isize name(void *data) +typedef WORKER_TASK_PROC(WorkerTaskProc); + +struct WorkerTask { + WorkerTaskProc *do_work; + void *data; + isize result; +}; + + +struct ThreadPool { + gbMutex mutex; + gbSemaphore sem_available; + gbAtomic32 processing_work_count; + bool is_running; + + gbAllocator allocator; + + WorkerTask *tasks; + isize volatile task_head; + isize volatile task_tail; + isize volatile task_capacity; + + gbThread *threads; + isize thread_count; + + char worker_prefix[10]; + i32 worker_prefix_len; +}; + +void thread_pool_init(ThreadPool *pool, gbAllocator const &a, isize thread_count, char const *worker_prefix = nullptr); +void thread_pool_destroy(ThreadPool *pool); +void thread_pool_start(ThreadPool *pool); +void thread_pool_join(ThreadPool *pool); +void thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data); +void thread_pool_kick(ThreadPool *pool); +void thread_pool_kick_and_wait(ThreadPool *pool); +GB_THREAD_PROC(worker_thread_internal); + +void thread_pool_init(ThreadPool *pool, gbAllocator const &a, isize thread_count, char const *worker_prefix) { + pool->allocator = a; + pool->task_head = 0; + pool->task_tail = 0; + pool->task_capacity = 1024; + pool->tasks = gb_alloc_array(a, WorkerTask, pool->task_capacity); + pool->thread_count = gb_max(thread_count, 0); + pool->threads = gb_alloc_array(a, gbThread, pool->thread_count); + gb_mutex_init(&pool->mutex); + gb_semaphore_init(&pool->sem_available); + pool->is_running = true; + + pool->worker_prefix_len = 0; + if (worker_prefix) { + i32 worker_prefix_len = cast(i32)gb_strlen(worker_prefix); + worker_prefix_len = gb_min(worker_prefix_len, 10); + gb_memmove(pool->worker_prefix, worker_prefix, worker_prefix_len); + pool->worker_prefix_len = worker_prefix_len; + } + + for (isize i = 0; i < pool->thread_count; i++) { + gbThread *t = &pool->threads[i]; + gb_thread_init(t); + t->user_index = i; + #if 0 + // TODO(bill): Fix this on Linux as it causes a seg-fault + if (pool->worker_prefix_len > 0) { + char worker_name[16] = {}; + gb_snprintf(worker_name, gb_size_of(worker_name), "%.*s%u", pool->worker_prefix_len, pool->worker_prefix, cast(u16)i); + gb_thread_set_name(t, worker_name); + } + #endif + } +} + +void thread_pool_start(ThreadPool *pool) { + for (isize i = 0; i < pool->thread_count; i++) { + gbThread *t = &pool->threads[i]; + gb_thread_start(t, worker_thread_internal, pool); + } +} + +void thread_pool_join(ThreadPool *pool) { + pool->is_running = false; + + gb_semaphore_post(&pool->sem_available, cast(i32)pool->thread_count); + + gb_yield(); + + for (isize i = 0; i < pool->thread_count; i++) { + gbThread *t = &pool->threads[i]; + gb_thread_join(t); + } +} + + +void thread_pool_destroy(ThreadPool *pool) { + thread_pool_join(pool); + + gb_semaphore_destroy(&pool->sem_available); + gb_mutex_destroy(&pool->mutex); + gb_free(pool->allocator, pool->threads); + pool->thread_count = 0; + gb_free(pool->allocator, pool->tasks); + pool->task_head = 0; + pool->task_tail = 0; + pool->task_capacity = 0; +} + + +void thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data) { + gb_mutex_lock(&pool->mutex); + + if (pool->task_tail == pool->task_capacity) { + isize new_cap = 2*pool->task_capacity + 8; + WorkerTask *new_tasks = gb_alloc_array(pool->allocator, WorkerTask, new_cap); + gb_memmove(new_tasks, pool->tasks, (pool->task_tail)*gb_size_of(WorkerTask)); + pool->tasks = new_tasks; + pool->task_capacity = new_cap; + } + WorkerTask task = {}; + task.do_work = proc; + task.data = data; + + pool->tasks[pool->task_tail++] = task; + gb_semaphore_post(&pool->sem_available, 1); + gb_mutex_unlock(&pool->mutex); +} + +bool thread_pool_try_and_pop_task(ThreadPool *pool, WorkerTask *task) { + bool got_task = false; + if (gb_mutex_try_lock(&pool->mutex)) { + if (pool->task_tail > pool->task_head) { + gb_atomic32_fetch_add(&pool->processing_work_count, +1); + *task = pool->tasks[pool->task_head++]; + got_task = true; + } + gb_mutex_unlock(&pool->mutex); + } + return got_task; +} +void thread_pool_do_work(ThreadPool *pool, WorkerTask *task) { + task->result = task->do_work(task->data); + gb_atomic32_fetch_add(&pool->processing_work_count, -1); +} + +void thread_pool_wait_to_process(ThreadPool *pool) { + while (pool->task_tail > pool->task_head || gb_atomic32_load(&pool->processing_work_count) != 0) { + WorkerTask task = {}; + if (thread_pool_try_and_pop_task(pool, &task)) { + thread_pool_do_work(pool, &task); + } + + // Safety-kick + if (pool->task_tail > pool->task_head && gb_atomic32_load(&pool->processing_work_count) == 0) { + gb_mutex_lock(&pool->mutex); + gb_semaphore_post(&pool->sem_available, cast(i32)(pool->task_tail-pool->task_head)); + gb_mutex_unlock(&pool->mutex); + } + + gb_yield(); + } + + thread_pool_join(pool); +} + + +GB_THREAD_PROC(worker_thread_internal) { + ThreadPool *pool = cast(ThreadPool *)thread->user_data; + while (pool->is_running) { + gb_semaphore_wait(&pool->sem_available); + + WorkerTask task = {}; + if (thread_pool_try_and_pop_task(pool, &task)) { + thread_pool_do_work(pool, &task); + } + } + // Cascade + gb_semaphore_release(&pool->sem_available); + + return 0; +} + diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a551f0545..4b0db7ac4 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -86,6 +86,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_package, "package"), \ TOKEN_KIND(Token_typeid, "typeid"), \ TOKEN_KIND(Token_when, "when"), \ + TOKEN_KIND(Token_where, "where"), \ TOKEN_KIND(Token_if, "if"), \ TOKEN_KIND(Token_else, "else"), \ TOKEN_KIND(Token_for, "for"), \ @@ -400,6 +401,15 @@ void syntax_error(Token token, char *fmt, ...) { va_end(va); } +void syntax_error(TokenPos pos, char *fmt, ...) { + va_list va; + va_start(va, fmt); + Token token = {}; + token.pos = pos; + syntax_error_va(token, fmt, va); + va_end(va); +} + void syntax_warning(Token token, char *fmt, ...) { va_list va; va_start(va, fmt); @@ -745,9 +755,11 @@ exponent: scan_mantissa(t, 10); } - if (t->curr_rune == 'i') { + switch (t->curr_rune) { + case 'i': case 'j': case 'k': token.kind = Token_Imag; advance_to_next_rune(t); + break; } end: diff --git a/src/types.cpp b/src/types.cpp index dc7ecb946..bef69ee30 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -32,6 +32,9 @@ enum BasicKind { Basic_complex64, Basic_complex128, + Basic_quaternion128, + Basic_quaternion256, + Basic_int, Basic_uint, Basic_uintptr, @@ -66,6 +69,7 @@ enum BasicKind { Basic_UntypedInteger, Basic_UntypedFloat, Basic_UntypedComplex, + Basic_UntypedQuaternion, Basic_UntypedString, Basic_UntypedRune, Basic_UntypedNil, @@ -82,17 +86,18 @@ enum BasicFlag { BasicFlag_Unsigned = GB_BIT(2), BasicFlag_Float = GB_BIT(3), BasicFlag_Complex = GB_BIT(4), - BasicFlag_Pointer = GB_BIT(5), - BasicFlag_String = GB_BIT(6), - BasicFlag_Rune = GB_BIT(7), - BasicFlag_Untyped = GB_BIT(8), + BasicFlag_Quaternion = GB_BIT(5), + BasicFlag_Pointer = GB_BIT(6), + BasicFlag_String = GB_BIT(7), + BasicFlag_Rune = GB_BIT(8), + BasicFlag_Untyped = GB_BIT(9), - BasicFlag_LLVM = GB_BIT(10), + BasicFlag_LLVM = GB_BIT(11), BasicFlag_EndianLittle = GB_BIT(13), BasicFlag_EndianBig = GB_BIT(14), - BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex, + BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex | BasicFlag_Quaternion, BasicFlag_Ordered = BasicFlag_Integer | BasicFlag_Float | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune, BasicFlag_OrderedNumeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Rune, BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune, @@ -107,21 +112,23 @@ struct BasicType { struct TypeStruct { Array<Entity *> fields; - Ast *node; - Scope * scope; - - Array<i64> offsets; - bool are_offsets_set; - bool are_offsets_being_processed; - bool is_packed; - bool is_raw_union; - bool is_polymorphic; - bool is_poly_specialized; + Array<String> tags; + Array<i64> offsets; + Ast * node; + Scope * scope; + Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; - i64 custom_align; // NOTE(bill): Only used in structs at the moment + i64 custom_align; Entity * names; + + bool are_offsets_set; + bool are_offsets_being_processed; + bool is_packed; + bool is_raw_union; + bool is_polymorphic; + bool is_poly_specialized; }; struct TypeUnion { @@ -131,12 +138,11 @@ struct TypeUnion { i64 variant_block_size; i64 custom_align; i64 tag_size; + Type * polymorphic_params; // Type_Tuple + Type * polymorphic_parent; bool no_nil; - - bool is_polymorphic; - bool is_poly_specialized; - Type * polymorphic_params; // Type_Tuple - Type * polymorphic_parent; + bool is_polymorphic; + bool is_poly_specialized; }; #define TYPE_KINDS \ @@ -184,7 +190,9 @@ struct TypeUnion { TYPE_KIND(Tuple, struct { \ Array<Entity *> variables; /* Entity_Variable */ \ Array<i64> offsets; \ + bool are_offsets_being_processed; \ bool are_offsets_set; \ + bool is_packed; \ }) \ TYPE_KIND(Proc, struct { \ Ast *node; \ @@ -195,9 +203,9 @@ struct TypeUnion { i32 result_count; \ Array<Type *> abi_compat_params; \ Type * abi_compat_result_type; \ - bool return_by_pointer; \ - bool variadic; \ i32 variadic_index; \ + bool variadic; \ + bool abi_types_set; \ bool require_results; \ bool c_vararg; \ bool is_polymorphic; \ @@ -205,6 +213,7 @@ struct TypeUnion { bool has_proc_default_values; \ bool has_named_results; \ bool diverging; /* no return */ \ + bool return_by_pointer; \ u64 tags; \ isize specialization_count; \ ProcCallingConvention calling_convention; \ @@ -341,6 +350,9 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_complex64, BasicFlag_Complex, 8, STR_LIT("complex64")}}, {Type_Basic, {Basic_complex128, BasicFlag_Complex, 16, STR_LIT("complex128")}}, + {Type_Basic, {Basic_quaternion128, BasicFlag_Quaternion, 16, STR_LIT("quaternion128")}}, + {Type_Basic, {Basic_quaternion256, BasicFlag_Quaternion, 32, STR_LIT("quaternion256")}}, + {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_uintptr, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uintptr")}}, @@ -376,6 +388,7 @@ gb_global Type basic_types[] = { {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_UntypedComplex, BasicFlag_Complex | BasicFlag_Untyped, 0, STR_LIT("untyped complex")}}, + {Type_Basic, {Basic_UntypedQuaternion, BasicFlag_Quaternion | BasicFlag_Untyped, 0, STR_LIT("untyped quaternion")}}, {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")}}, @@ -411,6 +424,9 @@ gb_global Type *t_f64 = &basic_types[Basic_f64]; gb_global Type *t_complex64 = &basic_types[Basic_complex64]; gb_global Type *t_complex128 = &basic_types[Basic_complex128]; +gb_global Type *t_quaternion128 = &basic_types[Basic_quaternion128]; +gb_global Type *t_quaternion256 = &basic_types[Basic_quaternion256]; + gb_global Type *t_int = &basic_types[Basic_int]; gb_global Type *t_uint = &basic_types[Basic_uint]; gb_global Type *t_uintptr = &basic_types[Basic_uintptr]; @@ -445,6 +461,7 @@ 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_complex = &basic_types[Basic_UntypedComplex]; +gb_global Type *t_untyped_quaternion = &basic_types[Basic_UntypedQuaternion]; 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]; @@ -471,6 +488,7 @@ gb_global Type *t_type_info_integer = nullptr; gb_global Type *t_type_info_rune = nullptr; gb_global Type *t_type_info_float = nullptr; gb_global Type *t_type_info_complex = nullptr; +gb_global Type *t_type_info_quaternion = nullptr; gb_global Type *t_type_info_any = nullptr; gb_global Type *t_type_info_typeid = nullptr; gb_global Type *t_type_info_string = nullptr; @@ -495,6 +513,7 @@ gb_global Type *t_type_info_integer_ptr = nullptr; gb_global Type *t_type_info_rune_ptr = nullptr; gb_global Type *t_type_info_float_ptr = nullptr; gb_global Type *t_type_info_complex_ptr = nullptr; +gb_global Type *t_type_info_quaternion_ptr = nullptr; gb_global Type *t_type_info_any_ptr = nullptr; gb_global Type *t_type_info_typeid_ptr = nullptr; gb_global Type *t_type_info_string_ptr = nullptr; @@ -535,8 +554,27 @@ i64 type_offset_of (Type *t, i32 index); gbString type_to_string (Type *type); void init_map_internal_types(Type *type); Type * bit_set_to_int(Type *t); +bool are_types_identical(Type *x, Type *y); +bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) { + if (ptr_set_exists(s, t)) { + return true; + } + + // TODO(bill, 2019-10-05): This is very slow and it's probably a lot + // faster to cache types correctly + for_array(i, s->entries) { + Type *f = s->entries[i].ptr; + if (are_types_identical(t, f)) { + ptr_set_add(s, t); + return true; + } + } + + return false; +} + Type *base_type(Type *t) { for (;;) { if (t == nullptr) { @@ -923,6 +961,13 @@ bool is_type_complex(Type *t) { } return false; } +bool is_type_quaternion(Type *t) { + t = core_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Quaternion) != 0; + } + return false; +} bool is_type_f32(Type *t) { t = core_type(t); if (t->kind == Type_Basic) { @@ -1037,14 +1082,40 @@ Type *core_array_type(Type *t) { return t; } +// NOTE(bill): type can be easily compared using memcmp +bool is_type_simple_compare(Type *t) { + t = core_type(t); + switch (t->kind) { + case Type_Array: + return is_type_simple_compare(t->Array.elem); + + case Type_Basic: + if (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Float|BasicFlag_Complex|BasicFlag_Rune|BasicFlag_Pointer)) { + return true; + } + return false; + + case Type_Pointer: + case Type_Proc: + case Type_BitSet: + case Type_BitField: + return true; + } + + return false; +} + Type *base_complex_elem_type(Type *t) { t = core_type(t); - if (is_type_complex(t)) { + if (t->kind == Type_Basic) { switch (t->Basic.kind) { - // case Basic_complex32: return t_f16; - case Basic_complex64: return t_f32; - case Basic_complex128: return t_f64; - case Basic_UntypedComplex: return t_untyped_float; + // case Basic_complex32: return t_f16; + case Basic_complex64: return t_f32; + case Basic_complex128: return t_f64; + case Basic_quaternion128: return t_f32; + case Basic_quaternion256: return t_f64; + case Basic_UntypedComplex: return t_untyped_float; + case Basic_UntypedQuaternion: return t_untyped_float; } } GB_PANIC("Invalid complex type"); @@ -1099,12 +1170,11 @@ bool is_type_integer_endian_big(Type *t) { return is_type_integer_endian_big(bit_set_to_int(t)); } else if (t->kind == Type_Pointer) { return is_type_integer_endian_big(&basic_types[Basic_uintptr]); - } else { - GB_PANIC("Unsupported type: %s", type_to_string(t)); } return build_context.endian_kind == TargetEndian_Big; } + bool is_type_integer_endian_little(Type *t) { t = core_type(t); if (t->kind == Type_Basic) { @@ -1118,11 +1188,24 @@ bool is_type_integer_endian_little(Type *t) { return is_type_integer_endian_little(bit_set_to_int(t)); } else if (t->kind == Type_Pointer) { return is_type_integer_endian_little(&basic_types[Basic_uintptr]); - } else { - GB_PANIC("Unsupported type: %s", type_to_string(t)); } return build_context.endian_kind == TargetEndian_Little; } +bool is_type_endian_big(Type *t) { + return is_type_integer_endian_big(t); +} +bool is_type_endian_little(Type *t) { + return is_type_integer_endian_little(t); +} + +bool is_type_dereferenceable(Type *t) { + if (is_type_rawptr(t)) { + return false; + } + return is_type_pointer(t); +} + + bool is_type_different_to_arch_endianness(Type *t) { switch (build_context.endian_kind) { @@ -1284,6 +1367,19 @@ bool is_type_indexable(Type *t) { return false; } +bool is_type_sliceable(Type *t) { + Type *bt = base_type(t); + switch (bt->kind) { + case Type_Basic: + return bt->Basic.kind == Basic_string; + case Type_Array: + case Type_Slice: + case Type_DynamicArray: + return true; + } + return false; +} + bool is_type_polymorphic_record(Type *t) { t = base_type(t); @@ -1663,6 +1759,12 @@ bool are_types_identical(Type *x, Type *y) { if (xf_is_using ^ yf_is_using) { return false; } + if (x->Struct.tags.count != y->Struct.tags.count) { + return false; + } + if (x->Struct.tags.count > 0 && x->Struct.tags[i] != y->Struct.tags[i]) { + return false; + } } return true; } @@ -1683,7 +1785,8 @@ bool are_types_identical(Type *x, Type *y) { case Type_Tuple: if (y->kind == Type_Tuple) { - if (x->Tuple.variables.count == y->Tuple.variables.count) { + if (x->Tuple.variables.count == y->Tuple.variables.count && + x->Tuple.is_packed == y->Tuple.is_packed) { for_array(i, x->Tuple.variables) { Entity *xe = x->Tuple.variables[i]; Entity *ye = y->Tuple.variables[i]; @@ -1763,6 +1866,7 @@ Type *default_type(Type *type) { case Basic_UntypedInteger: return t_int; case Basic_UntypedFloat: return t_f64; case Basic_UntypedComplex: return t_complex128; + case Basic_UntypedQuaternion: return t_quaternion256; case Basic_UntypedString: return t_string; case Basic_UntypedRune: return t_rune; } @@ -2131,19 +2235,22 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty if (type->Array.count <= 4) { // HACK(bill): Memory leak switch (type->Array.count) { - #define _ARRAY_FIELD_CASE(_length, _name) \ - case (_length): \ - if (field_name == _name) { \ + #define _ARRAY_FIELD_CASE_IF(_length, _name) \ + if (field_name == (_name)) { \ selection_add_index(&sel, (_length)-1); \ sel.entity = alloc_entity_array_elem(nullptr, make_token_ident(str_lit(_name)), type->Array.elem, (_length)-1); \ return sel; \ - } \ + } + #define _ARRAY_FIELD_CASE(_length, _name0, _name1) \ + case (_length): \ + _ARRAY_FIELD_CASE_IF(_length, _name0); \ + _ARRAY_FIELD_CASE_IF(_length, _name1); \ /*fallthrough*/ - _ARRAY_FIELD_CASE(4, "w"); - _ARRAY_FIELD_CASE(3, "z"); - _ARRAY_FIELD_CASE(2, "y"); - _ARRAY_FIELD_CASE(1, "x"); + _ARRAY_FIELD_CASE(4, "w", "a"); + _ARRAY_FIELD_CASE(3, "z", "b"); + _ARRAY_FIELD_CASE(2, "y", "g"); + _ARRAY_FIELD_CASE(1, "x", "r"); default: break; #undef _ARRAY_FIELD_CASE @@ -2254,7 +2361,9 @@ i64 type_size_of(Type *t) { return 0; } // NOTE(bill): Always calculate the size when it is a Type_Basic - if (t->kind != Type_Basic && t->cached_size >= 0) { + if (t->kind == Type_Named && t->cached_size >= 0) { + + } else if (t->kind != Type_Basic && t->cached_size >= 0) { return t->cached_size; } TypePath path = {0}; @@ -2269,7 +2378,9 @@ i64 type_align_of(Type *t) { return 1; } // NOTE(bill): Always calculate the size when it is a Type_Basic - if (t->kind != Type_Basic && t->cached_align > 0) { + if (t->kind == Type_Named && t->cached_align >= 0) { + + } if (t->kind != Type_Basic && t->cached_align > 0) { return t->cached_align; } @@ -2303,6 +2414,8 @@ i64 type_align_of_internal(Type *t, TypePath *path) { case Basic_complex64: case Basic_complex128: return type_size_of_internal(t, path) / 2; + case Basic_quaternion128: case Basic_quaternion256: + return type_size_of_internal(t, path) / 4; } } break; @@ -2488,9 +2601,9 @@ bool type_set_offsets(Type *t) { } } else if (is_type_tuple(t)) { if (!t->Tuple.are_offsets_set) { - t->Struct.are_offsets_being_processed = true; - t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, false, false); - t->Struct.are_offsets_being_processed = false; + t->Tuple.are_offsets_being_processed = true; + t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false); + t->Tuple.are_offsets_being_processed = false; t->Tuple.are_offsets_set = true; return true; } @@ -2913,35 +3026,54 @@ gbString write_type_to_string(gbString str, Type *type) { isize comma_index = 0; for_array(i, type->Tuple.variables) { Entity *var = type->Tuple.variables[i]; - if (var != nullptr) { - if (var->kind == Entity_Constant) { - // Ignore - continue; + if (var == nullptr) { + continue; + } + String name = var->token.string; + if (var->kind == Entity_Constant) { + str = gb_string_appendc(str, "$"); + str = gb_string_append_length(str, name.text, name.len); + if (!is_type_untyped(var->type)) { + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, var->type); + str = gb_string_appendc(str, " = "); + str = write_exact_value_to_string(str, var->Constant.value); + } else { + str = gb_string_appendc(str, "="); + str = write_exact_value_to_string(str, var->Constant.value); } + continue; + } - if (comma_index++ > 0) { - str = gb_string_appendc(str, ", "); - } + if (comma_index++ > 0) { + str = gb_string_appendc(str, ", "); + } - if (var->kind == Entity_Variable) { - if (var->flags&EntityFlag_CVarArg) { - str = gb_string_appendc(str, "#c_vararg "); - } - if (var->flags&EntityFlag_Ellipsis) { - Type *slice = base_type(var->type); - str = gb_string_appendc(str, ".."); - GB_ASSERT(var->type->kind == Type_Slice); - str = write_type_to_string(str, slice->Slice.elem); - } else { - str = write_type_to_string(str, var->type); - } + if (var->kind == Entity_Variable) { + if (var->flags&EntityFlag_CVarArg) { + str = gb_string_appendc(str, "#c_vararg "); + } + if (var->flags&EntityFlag_Ellipsis) { + Type *slice = base_type(var->type); + str = gb_string_appendc(str, ".."); + GB_ASSERT(var->type->kind == Type_Slice); + str = write_type_to_string(str, slice->Slice.elem); + } else { + str = write_type_to_string(str, var->type); + } + } else { + GB_ASSERT(var->kind == Entity_TypeName); + if (var->type->kind == Type_Generic) { + str = gb_string_appendc(str, "typeid/"); + str = write_type_to_string(str, var->type); } else { - GB_ASSERT(var->kind == Entity_TypeName); - if (var->type->kind == Type_Generic) { - str = gb_string_appendc(str, "type/"); + if (var->kind == Entity_TypeName) { + str = gb_string_appendc(str, "$"); + str = gb_string_append_length(str, name.text, name.len); + str = gb_string_appendc(str, "="); str = write_type_to_string(str, var->type); } else { - str = gb_string_appendc(str, "type"); + str = gb_string_appendc(str, "typeid"); } } } diff --git a/src/unicode.cpp b/src/unicode.cpp index 0ad658806..679d56365 100644 --- a/src/unicode.cpp +++ b/src/unicode.cpp @@ -2,7 +2,6 @@ #pragma warning(disable: 4245) extern "C" { -#include "utf8proc/utf8proc.h" #include "utf8proc/utf8proc.c" } #pragma warning(pop) diff --git a/src/utf8proc/utf8proc.c b/src/utf8proc/utf8proc.c index e821889c6..29fc250fc 100644 --- a/src/utf8proc/utf8proc.c +++ b/src/utf8proc/utf8proc.c @@ -40,7 +40,6 @@ * Implementation of libutf8proc. */ - #include "utf8proc.h" #include "utf8proc_data.c" |