aboutsummaryrefslogtreecommitdiff
path: root/src/checker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/checker.cpp')
-rw-r--r--src/checker.cpp371
1 files changed, 241 insertions, 130 deletions
diff --git a/src/checker.cpp b/src/checker.cpp
index 29f22bd9c..4d7514d0b 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -968,10 +968,11 @@ gb_internal void init_universal(void) {
add_global_bool_constant("true", true);
add_global_bool_constant("false", false);
- add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
- add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
- add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
+ add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
+ add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
+ add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME);
+ add_global_string_constant("ODIN_WINDOWS_SUBSYSTEM", bc->ODIN_WINDOWS_SUBSYSTEM);
{
GlobalEnumValue values[TargetOs_COUNT] = {
@@ -1084,7 +1085,7 @@ gb_internal void init_universal(void) {
add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp()));
- add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", lb_use_new_pass_system());
+ add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", lb_use_new_pass_system() && !is_arch_wasm());
{
GlobalEnumValue values[3] = {
@@ -2211,9 +2212,14 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
Entity *e = entry.value;
switch (bt->Struct.soa_kind) {
case StructSoa_Dynamic:
+ add_min_dep_type_info(c, t_type_info_ptr); // append_soa
+
add_min_dep_type_info(c, t_allocator);
/*fallthrough*/
case StructSoa_Slice:
+ add_min_dep_type_info(c, t_int);
+ add_min_dep_type_info(c, t_uint);
+ /*fallthrough*/
case StructSoa_Fixed:
add_min_dep_type_info(c, alloc_type_pointer(e->type));
break;
@@ -2289,7 +2295,6 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
if (entity->type != nullptr &&
is_type_polymorphic(entity->type)) {
-
DeclInfo *decl = decl_info_of_entity(entity);
if (decl != nullptr && decl->gen_proc_type == nullptr) {
return;
@@ -2341,98 +2346,7 @@ gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String co
}
-
-gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
- isize entity_count = c->info.entities.count;
- isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor
-
- ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap);
- map_init(&c->info.minimum_dependency_type_info_set);
-
-#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
- if (condition) { \
- String entities[] = {__VA_ARGS__}; \
- for (isize i = 0; i < gb_count_of(entities); i++) { \
- force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \
- } \
- } \
-} while (0)
-
- // required runtime entities
- FORCE_ADD_RUNTIME_ENTITIES(true,
- // Odin types
- str_lit("Source_Code_Location"),
- str_lit("Context"),
- str_lit("Allocator"),
- str_lit("Logger"),
-
- // Odin internal procedures
- str_lit("__init_context"),
- str_lit("cstring_to_string"),
- str_lit("_cleanup_runtime"),
-
- // Pseudo-CRT required procedures
- str_lit("memset"),
- str_lit("memcpy"),
- str_lit("memmove"),
-
- // Utility procedures
- str_lit("memory_equal"),
- str_lit("memory_compare"),
- str_lit("memory_compare_zero"),
- );
-
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend,
- // Extended data type internal procedures
- str_lit("umodti3"),
- str_lit("udivti3"),
- str_lit("modti3"),
- str_lit("divti3"),
- str_lit("fixdfti"),
- str_lit("fixunsdfti"),
- str_lit("fixunsdfdi"),
- str_lit("floattidf"),
- str_lit("floattidf_unsigned"),
- str_lit("truncsfhf2"),
- str_lit("truncdfhf2"),
- str_lit("gnu_h2f_ieee"),
- str_lit("gnu_f2h_ieee"),
- str_lit("extendhfsf2"),
-
- // WASM Specific
- str_lit("__ashlti3"),
- str_lit("__multi3"),
- );
-
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti,
- // Odin types
- str_lit("Type_Info"),
-
- // Global variables
- str_lit("type_table"),
- str_lit("__type_info_of"),
- );
-
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point,
- // Global variables
- str_lit("args__"),
- );
-
- FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()),
- // NOTE(bill): Only if these exist
- str_lit("_tls_index"),
- str_lit("_fltused"),
- );
-
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check,
- // Bounds checking related procedures
- str_lit("bounds_check_error"),
- str_lit("matrix_bounds_check_error"),
- str_lit("slice_expr_error_hi"),
- str_lit("slice_expr_error_lo_hi"),
- str_lit("multi_pointer_slice_expr_error"),
- );
-
+gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *start) {
for_array(i, c->info.definitions) {
Entity *e = c->info.definitions[i];
if (e->scope == builtin_pkg->scope) {
@@ -2575,6 +2489,104 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
start->flags |= EntityFlag_Used;
add_dependency_to_set(c, start);
}
+}
+
+gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
+ isize entity_count = c->info.entities.count;
+ isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor
+
+ ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap);
+ map_init(&c->info.minimum_dependency_type_info_set);
+
+#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
+ if (condition) { \
+ String entities[] = {__VA_ARGS__}; \
+ for (isize i = 0; i < gb_count_of(entities); i++) { \
+ force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \
+ } \
+ } \
+} while (0)
+
+ // required runtime entities
+ FORCE_ADD_RUNTIME_ENTITIES(true,
+ // Odin types
+ str_lit("Source_Code_Location"),
+ str_lit("Context"),
+ str_lit("Allocator"),
+ str_lit("Logger"),
+
+ // Odin internal procedures
+ str_lit("__init_context"),
+ str_lit("cstring_to_string"),
+ str_lit("_cleanup_runtime"),
+
+ // Pseudo-CRT required procedures
+ str_lit("memset"),
+ str_lit("memcpy"),
+ str_lit("memmove"),
+
+ // Utility procedures
+ str_lit("memory_equal"),
+ str_lit("memory_compare"),
+ str_lit("memory_compare_zero"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend,
+ // Extended data type internal procedures
+ str_lit("umodti3"),
+ str_lit("udivti3"),
+ str_lit("modti3"),
+ str_lit("divti3"),
+ str_lit("fixdfti"),
+ str_lit("fixunsdfti"),
+ str_lit("fixunsdfdi"),
+ str_lit("floattidf"),
+ str_lit("floattidf_unsigned"),
+ str_lit("truncsfhf2"),
+ str_lit("truncdfhf2"),
+ str_lit("gnu_h2f_ieee"),
+ str_lit("gnu_f2h_ieee"),
+ str_lit("extendhfsf2"),
+
+ // WASM Specific
+ str_lit("__ashlti3"),
+ str_lit("__multi3"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti,
+ // Odin types
+ str_lit("Type_Info"),
+
+ // Global variables
+ str_lit("type_table"),
+ str_lit("__type_info_of"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point,
+ // Global variables
+ str_lit("args__"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()),
+ // NOTE(bill): Only if these exist
+ str_lit("_tls_index"),
+ str_lit("_fltused"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check,
+ // Bounds checking related procedures
+ str_lit("bounds_check_error"),
+ str_lit("matrix_bounds_check_error"),
+ str_lit("slice_expr_error_hi"),
+ str_lit("slice_expr_error_lo_hi"),
+ str_lit("multi_pointer_slice_expr_error"),
+ );
+
+ add_dependency_to_set(c, c->info.instrumentation_enter_entity);
+ add_dependency_to_set(c, c->info.instrumentation_exit_entity);
+
+ generate_minimum_dependency_set_internal(c, start);
+
#undef FORCE_ADD_RUNTIME_ENTITIES
}
@@ -3405,6 +3417,39 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
+ } else if (name == "entry_point_only") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->entry_point_only = true;
+ return true;
+ } else if (name == "no_instrumentation") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Invalid) {
+ ac->no_instrumentation = Instrumentation_Disabled;
+ } else if (ev.kind == ExactValue_Bool) {
+ if (ev.value_bool) {
+ ac->no_instrumentation = Instrumentation_Disabled;
+ } else {
+ ac->no_instrumentation = Instrumentation_Enabled;
+ }
+ } else {
+ error(value, "Expected either a boolean or no parameter for '%.*s'", LIT(name));
+ return false;
+ }
+ return true;
+ } else if (name == "instrumentation_enter") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->instrumentation_enter = true;
+ return true;
+ } else if (name == "instrumentation_exit") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->instrumentation_exit = true;
+ return true;
}
return false;
}
@@ -4014,12 +4059,11 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
if (c->foreign_context.default_cc > 0) {
cc = c->foreign_context.default_cc;
} else if (is_arch_wasm()) {
- begin_error_block();
+ ERROR_BLOCK();
error(init, "For wasm related targets, it is required that you either define the"
" @(default_calling_convention=<string>) on the foreign block or"
" explicitly assign it on the procedure signature");
error_line("\tSuggestion: when dealing with normal Odin code (e.g. js_wasm32), use \"contextless\"; when dealing with Emscripten like code, use \"c\"\n");
- end_error_block();
}
}
e->Procedure.link_prefix = c->foreign_context.link_prefix;
@@ -4066,8 +4110,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
if (e->kind != Entity_Procedure) {
if (fl != nullptr) {
- begin_error_block();
- defer (end_error_block());
+ ERROR_BLOCK();
AstKind kind = init->kind;
error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_strings[kind]));
@@ -4733,7 +4776,7 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
}
if (has_asm_extension(fullpath)) {
- if (build_context.metrics.arch != TargetArch_amd64) {
+ if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.os != TargetOs_darwin) {
error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s",
LIT(target_os_names[build_context.metrics.os]), LIT(target_arch_names[build_context.metrics.arch]));
}
@@ -5304,6 +5347,44 @@ gb_internal void calculate_global_init_order(Checker *c) {
}
}
+gb_internal void check_procedure_later_from_entity(Checker *c, Entity *e, char const *from_msg) {
+ if (e == nullptr || e->kind != Entity_Procedure) {
+ return;
+ }
+ if (e->Procedure.is_foreign) {
+ return;
+ }
+ if ((e->flags & EntityFlag_ProcBodyChecked) != 0) {
+ return;
+ }
+ Type *type = base_type(e->type);
+ GB_ASSERT(type->kind == Type_Proc);
+
+ if (is_type_polymorphic(type) && !type->Proc.is_poly_specialized) {
+ return;
+ }
+
+ GB_ASSERT(e->decl_info != nullptr);
+
+ ProcInfo *pi = gb_alloc_item(permanent_allocator(), ProcInfo);
+ pi->file = e->file;
+ pi->token = e->token;
+ pi->decl = e->decl_info;
+ pi->type = e->type;
+
+ Ast *pl = e->decl_info->proc_lit;
+ GB_ASSERT(pl != nullptr);
+ pi->body = pl->ProcLit.body;
+ pi->tags = pl->ProcLit.tags;
+ if (pi->body == nullptr) {
+ return;
+ }
+ if (from_msg != nullptr) {
+ debugf("CHECK PROCEDURE LATER [FROM %s]! %.*s :: %s {...}\n", from_msg, LIT(e->token.string), type_to_string(e->type));
+ }
+ check_procedure_later(c, pi);
+}
+
gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped) {
if (pi == nullptr) {
@@ -5410,6 +5491,15 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u
add_untyped_expressions(&c->info, ctx.untyped);
+ rw_mutex_shared_lock(&ctx.decl->deps_mutex);
+ for (Entity *dep : ctx.decl->deps) {
+ if (dep && dep->kind == Entity_Procedure &&
+ (dep->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later_from_entity(c, dep, NULL);
+ }
+ }
+ rw_mutex_shared_unlock(&ctx.decl->deps_mutex);
+
return true;
}
@@ -5432,30 +5522,7 @@ gb_internal void check_unchecked_bodies(Checker *c) {
global_procedure_body_in_worker_queue = false;
for (Entity *e : c->info.minimum_dependency_set) {
- if (e == nullptr || e->kind != Entity_Procedure) {
- continue;
- }
- if (e->Procedure.is_foreign) {
- continue;
- }
- if ((e->flags & EntityFlag_ProcBodyChecked) == 0) {
- GB_ASSERT(e->decl_info != nullptr);
-
- ProcInfo *pi = gb_alloc_item(permanent_allocator(), ProcInfo);
- pi->file = e->file;
- pi->token = e->token;
- pi->decl = e->decl_info;
- pi->type = e->type;
-
- Ast *pl = e->decl_info->proc_lit;
- GB_ASSERT(pl != nullptr);
- pi->body = pl->ProcLit.body;
- pi->tags = pl->ProcLit.tags;
- if (pi->body == nullptr) {
- continue;
- }
- check_procedure_later(c, pi);
- }
+ check_procedure_later_from_entity(c, e, "check_unchecked_bodies");
}
if (!global_procedure_body_in_worker_queue) {
@@ -5497,7 +5564,28 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) {
}
}
+gb_internal GB_COMPARE_PROC(init_procedures_cmp);
+gb_internal GB_COMPARE_PROC(fini_procedures_cmp);
+
+gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Array<Entity *> *array) {
+ Entity *prev = nullptr;
+
+ for (isize i = 0; i < array->count; /**/) {
+ Entity *curr = array->data[i];
+ if (prev == curr) {
+ array_ordered_remove(array, i);
+ } else {
+ prev = curr;
+ i += 1;
+ }
+ }
+}
+
+
gb_internal void check_test_procedures(Checker *c) {
+ gb_sort_array(c->info.testing_procedures.data, c->info.testing_procedures.count, init_procedures_cmp);
+ remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures);
+
if (build_context.test_names.entries.count == 0) {
return;
}
@@ -5943,10 +6031,14 @@ gb_internal GB_COMPARE_PROC(fini_procedures_cmp) {
return init_procedures_cmp(b, a);
}
-
gb_internal void check_sort_init_and_fini_procedures(Checker *c) {
gb_sort_array(c->info.init_procedures.data, c->info.init_procedures.count, init_procedures_cmp);
gb_sort_array(c->info.fini_procedures.data, c->info.fini_procedures.count, fini_procedures_cmp);
+
+ // NOTE(bill): remove possible duplicates from the init/fini lists
+ // NOTE(bill): because the arrays are sorted, you only need to check the previous element
+ remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.init_procedures);
+ remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.fini_procedures);
}
gb_internal void add_type_info_for_type_definitions(Checker *c) {
@@ -6091,9 +6183,6 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);
- TIME_SECTION("check test procedures");
- check_test_procedures(c);
-
TIME_SECTION("add type info for type definitions");
add_type_info_for_type_definitions(c);
check_merge_queues_into_arrays(c);
@@ -6108,7 +6197,18 @@ gb_internal void check_parsed_files(Checker *c) {
check_unchecked_bodies(c);
check_merge_queues_into_arrays(c);
+ thread_pool_wait();
+
+ TIME_SECTION("update minimum dependency set");
+ generate_minimum_dependency_set_internal(c, c->info.entry_point);
+
+ // NOTE(laytan): has to be ran after generate_minimum_dependency_set,
+ // because that collects the test procedures.
+ TIME_SECTION("check test procedures");
+ check_test_procedures(c);
+ check_merge_queues_into_arrays(c);
+ thread_pool_wait();
TIME_SECTION("check entry point");
if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
@@ -6150,6 +6250,17 @@ gb_internal void check_parsed_files(Checker *c) {
GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0);
GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0);
+ TIME_SECTION("check instrumentation calls");
+ {
+ if ((c->info.instrumentation_enter_entity != nullptr) ^
+ (c->info.instrumentation_exit_entity != nullptr)) {
+ Entity *e = c->info.instrumentation_enter_entity;
+ if (!e) e = c->info.instrumentation_exit_entity;
+ error(e->token, "Both @(instrumentation_enter) and @(instrumentation_exit) must be defined");
+ }
+ }
+
+
TIME_SECTION("add untyped expression values");
// Add untyped expression values
for (UntypedExprInfo u = {}; mpsc_dequeue(&c->global_untyped_queue, &u); /**/) {