aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChris Heyes <rumcode@icloud.com>2019-11-01 19:18:33 +0000
committerChris Heyes <rumcode@icloud.com>2019-11-01 19:18:33 +0000
commit153e7525b5d64f12deb318d50aee1d9dbeb1fc8e (patch)
treeb8ac88c0788af31bcb3c5041d78306c2120714f6 /src
parentd85893954dccc7833e3d954db641d9fd2a3c7451 (diff)
parent44a303e5778fb8564964d53523634f34f8589489 (diff)
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.cpp56
-rw-r--r--src/check_decl.cpp247
-rw-r--r--src/check_expr.cpp1163
-rw-r--r--src/check_stmt.cpp221
-rw-r--r--src/check_type.cpp400
-rw-r--r--src/checker.cpp166
-rw-r--r--src/checker.hpp220
-rw-r--r--src/checker_builtin_procs.hpp320
-rw-r--r--src/common.cpp8
-rw-r--r--src/entity.cpp13
-rw-r--r--src/exact_value.cpp264
-rw-r--r--src/gb/gb.h30
-rw-r--r--src/ir.cpp963
-rw-r--r--src/ir_print.cpp300
-rw-r--r--src/main.cpp115
-rw-r--r--src/parser.cpp524
-rw-r--r--src/parser.hpp60
-rw-r--r--src/priority_queue.cpp4
-rw-r--r--src/range_cache.cpp70
-rw-r--r--src/string.cpp86
-rw-r--r--src/thread_pool.cpp184
-rw-r--r--src/tokenizer.cpp14
-rw-r--r--src/types.cpp272
-rw-r--r--src/unicode.cpp1
-rw-r--r--src/utf8proc/utf8proc.c1
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 &params,
+void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<String> *tags, Array<Ast *> const &params,
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(&params, 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(&params, 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(&params, 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"