aboutsummaryrefslogtreecommitdiff
path: root/src/checker.cpp
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2025-01-06 13:43:01 +0000
committerGitHub <noreply@github.com>2025-01-06 13:43:01 +0000
commit6e49bbb66853b5d824ac5bbd534ae3e81c4f39aa (patch)
tree50886a3be8f2fcfab053e07cfe9e15f50fa5f9f6 /src/checker.cpp
parentbd96cd0af761994210018ca647eb843dfeb71494 (diff)
parent98efb03934b464a1b23759b5695a12ff37588357 (diff)
Merge branch 'master' into d3d11-annotations
Diffstat (limited to 'src/checker.cpp')
-rw-r--r--src/checker.cpp1263
1 files changed, 899 insertions, 364 deletions
diff --git a/src/checker.cpp b/src/checker.cpp
index 29f22bd9c..5d3263789 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -3,6 +3,8 @@
#include "entity.cpp"
#include "types.cpp"
+String get_final_microarchitecture();
+
gb_internal void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t);
@@ -48,15 +50,6 @@ gb_internal bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *f
return check_rtti_type_disallowed(ast_token(expr), type, format);
}
-gb_internal void scope_reset(Scope *scope) {
- if (scope == nullptr) return;
-
- rw_mutex_lock(&scope->mutex);
- scope->head_child.store(nullptr, std::memory_order_relaxed);
- string_map_clear(&scope->elements);
- ptr_set_clear(&scope->imported);
- rw_mutex_unlock(&scope->mutex);
-}
gb_internal void scope_reserve(Scope *scope, isize count) {
string_map_reserve(&scope->elements, 2*count);
@@ -166,9 +159,6 @@ gb_internal void import_graph_node_swap(ImportGraphNode **data, isize i, isize j
}
-
-
-
gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
gb_zero_item(d);
if (parent) {
@@ -182,6 +172,9 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
ptr_set_init(&d->deps, 0);
ptr_set_init(&d->type_info_deps, 0);
d->labels.allocator = heap_allocator();
+ d->variadic_reuses.allocator = heap_allocator();
+ d->variadic_reuse_max_bytes = 0;
+ d->variadic_reuse_max_align = 1;
}
gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) {
@@ -224,7 +217,7 @@ gb_internal Scope *create_scope(CheckerInfo *info, Scope *parent) {
if (parent != nullptr && parent != builtin_pkg->scope) {
Scope *prev_head_child = parent->head_child.exchange(s, std::memory_order_acq_rel);
if (prev_head_child) {
- prev_head_child->next.store(s, std::memory_order_release);
+ s->next.store(prev_head_child, std::memory_order_release);
}
}
@@ -313,6 +306,7 @@ gb_internal void add_scope(CheckerContext *c, Ast *node, Scope *scope) {
case Ast_StructType: node->StructType.scope = scope; break;
case Ast_UnionType: node->UnionType.scope = scope; break;
case Ast_EnumType: node->EnumType.scope = scope; break;
+ case Ast_BitFieldType: node->BitFieldType.scope = scope; break;
default: GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
}
}
@@ -334,6 +328,7 @@ gb_internal Scope *scope_of_node(Ast *node) {
case Ast_StructType: return node->StructType.scope;
case Ast_UnionType: return node->UnionType.scope;
case Ast_EnumType: return node->EnumType.scope;
+ case Ast_BitFieldType: return node->BitFieldType.scope;
}
GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
return nullptr;
@@ -342,6 +337,7 @@ gb_internal Scope *scope_of_node(Ast *node) {
gb_internal void check_open_scope(CheckerContext *c, Ast *node) {
node = unparen_expr(node);
+ GB_ASSERT(node != nullptr);
GB_ASSERT(node->kind == Ast_Invalid ||
is_ast_stmt(node) ||
is_ast_type(node));
@@ -355,6 +351,7 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) {
case Ast_EnumType:
case Ast_UnionType:
case Ast_BitSetType:
+ case Ast_BitFieldType:
scope->flags |= ScopeFlag_Type;
break;
}
@@ -375,6 +372,7 @@ gb_internal Entity *scope_lookup_current(Scope *s, String const &name) {
return nullptr;
}
+
gb_internal void scope_lookup_parent(Scope *scope, String const &name, Scope **scope_, Entity **entity_) {
if (scope != nullptr) {
bool gone_thru_proc = false;
@@ -502,9 +500,15 @@ end:;
return result;
}
+gb_global bool in_single_threaded_checker_stage = false;
+
gb_internal Entity *scope_insert(Scope *s, Entity *entity) {
String name = entity->token.string;
- return scope_insert_with_name(s, name, entity);
+ if (in_single_threaded_checker_stage) {
+ return scope_insert_with_name_no_mutex(s, name, entity);
+ } else {
+ return scope_insert_with_name(s, name, entity);
+ }
}
gb_internal Entity *scope_insert_no_mutex(Scope *s, Entity *entity) {
@@ -513,7 +517,7 @@ gb_internal Entity *scope_insert_no_mutex(Scope *s, Entity *entity) {
}
-GB_COMPARE_PROC(entity_variable_pos_cmp) {
+gb_internal GB_COMPARE_PROC(entity_variable_pos_cmp) {
Entity *x = *cast(Entity **)a;
Entity *y = *cast(Entity **)b;
@@ -529,20 +533,32 @@ gb_internal u64 check_vet_flags(CheckerContext *c) {
c->curr_proc_decl->proc_lit) {
file = c->curr_proc_decl->proc_lit->file();
}
- if (file && file->vet_flags_set) {
- return file->vet_flags;
- }
- return build_context.vet_flags;
+
+ return ast_file_vet_flags(file);
}
gb_internal u64 check_vet_flags(Ast *node) {
AstFile *file = node->file();
- if (file && file->vet_flags_set) {
- return file->vet_flags;
+ return ast_file_vet_flags(file);
+}
+
+gb_internal u64 check_feature_flags(CheckerContext *c, Ast *node) {
+ AstFile *file = c->file;
+ if (file == nullptr &&
+ c->curr_proc_decl &&
+ c->curr_proc_decl->proc_lit) {
+ file = c->curr_proc_decl->proc_lit->file();
+ }
+ if (file == nullptr) {
+ file = node->file();
}
- return build_context.vet_flags;
+ if (file != nullptr && file->feature_flags_set) {
+ return file->feature_flags;
+ }
+ return 0;
}
+
enum VettedEntityKind {
VettedEntity_Invalid,
@@ -649,7 +665,7 @@ gb_internal bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) {
}
}
- zero_item(ve);
+ gb_zero_item(ve);
ve->kind = VettedEntity_Shadowed;
ve->entity = e;
ve->other = shadowed;
@@ -668,7 +684,7 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
}
case Entity_ImportName:
case Entity_LibraryName:
- zero_item(ve);
+ gb_zero_item(ve);
ve->kind = VettedEntity_Unused;
ve->entity = e;
return true;
@@ -677,20 +693,48 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
return false;
}
-gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
- bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
- bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
-
+gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_flags, bool per_entity) {
+ u64 original_vet_flags = vet_flags;
Array<VettedEntity> vetted_entities = {};
array_init(&vetted_entities, heap_allocator());
+ defer (array_free(&vetted_entities));
rw_mutex_shared_lock(&scope->mutex);
for (auto const &entry : scope->elements) {
Entity *e = entry.value;
if (e == nullptr) continue;
+
+ vet_flags = original_vet_flags;
+ if (per_entity) {
+ vet_flags = ast_file_vet_flags(e->file);
+ }
+
+ bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
+ bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
+ bool vet_unused_procedures = (vet_flags & VetFlag_UnusedProcedures) != 0;
+ if (vet_unused_procedures && e->pkg && e->pkg->kind == Package_Runtime) {
+ vet_unused_procedures = false;
+ }
+
VettedEntity ve_unused = {};
VettedEntity ve_shadowed = {};
- bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused);
+ bool is_unused = false;
+ if (vet_unused && check_vet_unused(c, e, &ve_unused)) {
+ is_unused = true;
+ } else if (vet_unused_procedures &&
+ e->kind == Entity_Procedure) {
+ if (e->flags&EntityFlag_Used) {
+ is_unused = false;
+ } else if (e->flags & EntityFlag_Require) {
+ is_unused = false;
+ } else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") {
+ is_unused = false;
+ } else {
+ is_unused = true;
+ ve_unused.kind = VettedEntity_Unused;
+ ve_unused.entity = e;
+ }
+ }
bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed);
if (is_unused && is_shadowed) {
VettedEntity ve_both = ve_shadowed;
@@ -700,23 +744,43 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
array_add(&vetted_entities, ve_unused);
} else if (is_shadowed) {
array_add(&vetted_entities, ve_shadowed);
+ } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using|EntityFlag_Static)) == 0 && !e->Variable.is_global) {
+ i64 sz = type_size_of(e->type);
+ // TODO(bill): When is a good size warn?
+ // Is >256 KiB good enough?
+ if (sz > 1ll<<18) {
+ gbString type_str = type_to_string(e->type);
+ warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(e->token.string), type_str, cast(long long)sz);
+ gb_string_free(type_str);
+ }
}
}
rw_mutex_shared_unlock(&scope->mutex);
- gb_sort(vetted_entities.data, vetted_entities.count, gb_size_of(VettedEntity), vetted_entity_variable_pos_cmp);
+ array_sort(vetted_entities, vetted_entity_variable_pos_cmp);
for (auto const &ve : vetted_entities) {
Entity *e = ve.entity;
Entity *other = ve.other;
String name = e->token.string;
+ vet_flags = original_vet_flags;
+ if (per_entity) {
+ vet_flags = ast_file_vet_flags(e->file);
+ }
+
if (ve.kind == VettedEntity_Shadowed_And_Unused) {
error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
} else if (vet_flags) {
switch (ve.kind) {
case VettedEntity_Unused:
- if (vet_flags & VetFlag_Unused) {
+ if (e->kind == Entity_Variable && (vet_flags & VetFlag_UnusedVariables) != 0) {
+ error(e->token, "'%.*s' declared but not used", LIT(name));
+ }
+ if (e->kind == Entity_Procedure && (vet_flags & VetFlag_UnusedProcedures) != 0) {
+ error(e->token, "'%.*s' declared but not used", LIT(name));
+ }
+ if ((e->kind == Entity_ImportName || e->kind == Entity_LibraryName) && (vet_flags & VetFlag_UnusedImports) != 0) {
error(e->token, "'%.*s' declared but not used", LIT(name));
}
break;
@@ -731,20 +795,13 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
break;
}
}
-
- if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using)) == 0) {
- i64 sz = type_size_of(e->type);
- // TODO(bill): When is a good size warn?
- // Is 128 KiB good enough?
- if (sz >= 1ll<<17) {
- gbString type_str = type_to_string(e->type);
- warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(name), type_str, cast(long long)sz);
- gb_string_free(type_str);
- }
- }
}
- array_free(&vetted_entities);
+}
+
+
+gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
+ check_scope_usage_internal(c, scope, vet_flags, false);
for (Scope *child = scope->head_child; child != nullptr; child = child->next) {
if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) {
@@ -770,15 +827,17 @@ gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *
rw_mutex_unlock(&d->type_info_deps_mutex);
}
-gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) {
+
+gb_internal AstPackage *get_runtime_package(CheckerInfo *info) {
+ String name = str_lit("runtime");
gbAllocator a = heap_allocator();
- String path = get_fullpath_core(a, name);
+ String path = get_fullpath_base_collection(a, name, nullptr);
defer (gb_free(a, path.text));
auto found = string_map_get(&info->packages, path);
if (found == nullptr) {
gb_printf_err("Name: %.*s\n", LIT(name));
gb_printf_err("Fullpath: %.*s\n", LIT(path));
-
+
for (auto const &entry : info->packages) {
gb_printf_err("%.*s\n", LIT(entry.key));
}
@@ -787,14 +846,37 @@ gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) {
return *found;
}
+gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) {
+ if (name == "runtime") {
+ return get_runtime_package(info);
+ }
-gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name) {
+ gbAllocator a = heap_allocator();
+ String path = get_fullpath_core_collection(a, name, nullptr);
+ defer (gb_free(a, path.text));
+ auto found = string_map_get(&info->packages, path);
+ if (found == nullptr) {
+ gb_printf_err("Name: %.*s\n", LIT(name));
+ gb_printf_err("Fullpath: %.*s\n", LIT(path));
+
+ for (auto const &entry : info->packages) {
+ gb_printf_err("%.*s\n", LIT(entry.key));
+ }
+ GB_ASSERT_MSG(found != nullptr, "Missing core package %.*s", LIT(name));
+ }
+ return *found;
+}
+
+gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name, bool required=false) {
String n = make_string_c(name);
AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
Entity *e = scope_lookup(p->scope, n);
GB_ASSERT_MSG(e != nullptr, "%s", name);
GB_ASSERT(c->decl != nullptr);
e->flags |= EntityFlag_Used;
+ if (required) {
+ e->flags |= EntityFlag_Require;
+ }
add_dependency(c->info, c->decl, e);
}
@@ -815,6 +897,10 @@ gb_internal void add_declaration_dependency(CheckerContext *c, Entity *e) {
if (e == nullptr) {
return;
}
+ if (e->flags & EntityFlag_Disabled) {
+ // ignore the dependencies if it has been `@(disabled=true)`
+ return;
+ }
if (c->decl != nullptr) {
add_dependency(c->info, c->decl, e);
}
@@ -968,10 +1054,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] = {
@@ -981,9 +1068,12 @@ gb_internal void init_universal(void) {
{"Linux", TargetOs_linux},
{"Essence", TargetOs_essence},
{"FreeBSD", TargetOs_freebsd},
+ {"Haiku", TargetOs_haiku},
{"OpenBSD", TargetOs_openbsd},
+ {"NetBSD", TargetOs_netbsd},
{"WASI", TargetOs_wasi},
{"JS", TargetOs_js},
+ {"Orca", TargetOs_orca},
{"Freestanding", TargetOs_freestanding},
};
@@ -1001,17 +1091,21 @@ gb_internal void init_universal(void) {
{"arm64", TargetArch_arm64},
{"wasm32", TargetArch_wasm32},
{"wasm64p32", TargetArch_wasm64p32},
+ {"riscv64", TargetArch_riscv64},
};
auto fields = add_global_enum_type(str_lit("Odin_Arch_Type"), values, gb_count_of(values));
add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch);
add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]);
}
+
+ add_global_string_constant("ODIN_MICROARCH_STRING", get_final_microarchitecture());
{
GlobalEnumValue values[BuildMode_COUNT] = {
{"Executable", BuildMode_Executable},
{"Dynamic", BuildMode_DynamicLibrary},
+ {"Static", BuildMode_StaticLibrary},
{"Object", BuildMode_Object},
{"Assembly", BuildMode_Assembly},
{"LLVM_IR", BuildMode_LLVM_IR},
@@ -1067,24 +1161,59 @@ gb_internal void init_universal(void) {
scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name);
}
+ {
+ int minimum_os_version = 0;
+ if (build_context.minimum_os_version_string != "") {
+ int major, minor, revision = 0;
+ #if defined(GB_SYSTEM_WINDOWS)
+ sscanf_s(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision);
+ #else
+ sscanf(cast(const char *)(build_context.minimum_os_version_string.text), "%d.%d.%d", &major, &minor, &revision);
+ #endif
+ minimum_os_version = (major*10000)+(minor*100)+revision;
+ }
+ add_global_constant("ODIN_MINIMUM_OS_VERSION", t_untyped_integer, exact_value_i64(minimum_os_version));
+ }
+
+ add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
+ add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
+ add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR);
+ add_global_bool_constant("ODIN_NO_BOUNDS_CHECK", build_context.no_bounds_check);
+ add_global_bool_constant("ODIN_NO_TYPE_ASSERT", build_context.no_type_assert);
+ add_global_bool_constant("ODIN_DEFAULT_TO_PANIC_ALLOCATOR", bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR);
+ add_global_bool_constant("ODIN_NO_CRT", bc->no_crt);
+ add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules);
+ add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test);
+ add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point);
+ add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES);
+ add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti);
+
+ add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT);
+ add_global_bool_constant("ODIN_TILDE", bc->tilde_backend);
- add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
- add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
- add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR);
- add_global_bool_constant("ODIN_NO_DYNAMIC_LITERALS", bc->no_dynamic_literals);
- add_global_bool_constant("ODIN_NO_CRT", bc->no_crt);
- add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules);
- add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test);
- add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point);
- add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES);
- add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti);
+ add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp()));
- add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT);
- add_global_bool_constant("ODIN_TILDE", bc->tilde_backend);
+ {
+ String version = {};
- add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp()));
+ #ifdef GIT_SHA
+ version.text = cast(u8 *)GIT_SHA;
+ version.len = gb_strlen(GIT_SHA);
+ #endif
- add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", lb_use_new_pass_system());
+ add_global_string_constant("ODIN_VERSION_HASH", version);
+ }
+
+ {
+ bool f16_supported = lb_use_new_pass_system();
+ if (is_arch_wasm()) {
+ f16_supported = false;
+ } else if (build_context.metrics.os == TargetOs_darwin && build_context.metrics.arch == TargetArch_amd64) {
+ // NOTE(laytan): See #3222 for my ramblings on this.
+ f16_supported = false;
+ }
+ add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", f16_supported);
+ }
{
GlobalEnumValue values[3] = {
@@ -1112,6 +1241,18 @@ gb_internal void init_universal(void) {
add_global_constant("ODIN_SANITIZER_FLAGS", named_type, exact_value_u64(bc->sanitizer_flags));
}
+ {
+ GlobalEnumValue values[5] = {
+ {"None", -1},
+ {"Minimal", 0},
+ {"Size", 1},
+ {"Speed", 2},
+ {"Aggressive", 3},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Optimization_Mode"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_OPTIMIZATION_MODE", bc->optimization_level);
+ }
// Builtin Procedures
@@ -1173,7 +1314,7 @@ gb_internal void init_universal(void) {
}
if (defined_values_double_declaration) {
- gb_exit(1);
+ exit_with_errors();
}
@@ -1187,9 +1328,9 @@ gb_internal void init_universal(void) {
// intrinsics types for objective-c stuff
{
- t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct());
- t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct());
- t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct());
+ t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct_complete());
+ t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct_complete());
+ t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct_complete());
t_objc_id = alloc_type_pointer(t_objc_object);
t_objc_SEL = alloc_type_pointer(t_objc_selector);
@@ -1220,6 +1361,7 @@ gb_internal void init_checker_info(CheckerInfo *i) {
array_init(&i->init_procedures, a, 0, 0);
array_init(&i->fini_procedures, a, 0, 0);
array_init(&i->required_foreign_imports_through_force, a, 0, 0);
+ array_init(&i->defineables, a);
map_init(&i->objc_msgSend_types);
string_map_init(&i->load_file_cache);
@@ -1229,7 +1371,12 @@ gb_internal void init_checker_info(CheckerInfo *i) {
mpsc_init(&i->definition_queue, a); //); // 1<<20);
mpsc_init(&i->required_global_variable_queue, a); // 1<<10);
mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10);
+ mpsc_init(&i->foreign_imports_to_check_fullpaths, a); // 1<<10);
+ mpsc_init(&i->foreign_decls_to_check, a); // 1<<10);
mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used
+
+ string_map_init(&i->load_directory_cache);
+ map_init(&i->load_directory_map);
}
gb_internal void destroy_checker_info(CheckerInfo *i) {
@@ -1245,14 +1392,19 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
string_map_destroy(&i->packages);
array_free(&i->variable_init_order);
array_free(&i->required_foreign_imports_through_force);
+ array_free(&i->defineables);
mpsc_destroy(&i->entity_queue);
mpsc_destroy(&i->definition_queue);
mpsc_destroy(&i->required_global_variable_queue);
mpsc_destroy(&i->required_foreign_imports_through_force_queue);
+ mpsc_destroy(&i->foreign_imports_to_check_fullpaths);
+ mpsc_destroy(&i->foreign_decls_to_check);
map_destroy(&i->objc_msgSend_types);
string_map_destroy(&i->load_file_cache);
+ string_map_destroy(&i->load_directory_cache);
+ map_destroy(&i->load_directory_map);
}
gb_internal CheckerContext make_checker_context(Checker *c) {
@@ -1290,7 +1442,7 @@ gb_internal void reset_checker_context(CheckerContext *ctx, AstFile *file, Untyp
auto type_path = ctx->type_path;
array_clear(type_path);
- zero_size(&ctx->pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg));
+ gb_zero_size(&ctx->pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg));
ctx->file = nullptr;
ctx->scope = builtin_pkg->scope;
@@ -1326,6 +1478,7 @@ gb_internal void init_checker(Checker *c) {
array_init(&c->nested_proc_lits, heap_allocator(), 0, 1<<20);
mpsc_init(&c->global_untyped_queue, a); // , 1<<20);
+ mpsc_init(&c->soa_types_to_complete, a); // , 1<<20);
c->builtin_ctx = make_checker_context(c);
}
@@ -1338,6 +1491,7 @@ gb_internal void destroy_checker(Checker *c) {
array_free(&c->nested_proc_lits);
array_free(&c->procs_to_check);
mpsc_destroy(&c->global_untyped_queue);
+ mpsc_destroy(&c->soa_types_to_complete);
}
@@ -1372,6 +1526,7 @@ gb_internal Entity *implicit_entity_of_node(Ast *clause) {
}
gb_internal Entity *entity_of_node(Ast *expr) {
+retry:;
expr = unparen_expr(expr);
switch (expr->kind) {
case_ast_node(ident, Ident, expr);
@@ -1388,6 +1543,21 @@ gb_internal Entity *entity_of_node(Ast *expr) {
case_ast_node(cc, CaseClause, expr);
return cc->implicit_entity;
case_end;
+
+ case_ast_node(ce, CallExpr, expr);
+ return ce->entity_procedure_of;
+ case_end;
+
+ case_ast_node(we, TernaryWhenExpr, expr);
+ if (we->cond == nullptr) {
+ break;
+ }
+ if (we->cond->tav.value.kind != ExactValue_Bool) {
+ break;
+ }
+ expr = we->cond->tav.value.value_bool ? we->x : we->y;
+ goto retry;
+ case_end;
}
return nullptr;
}
@@ -1535,9 +1705,9 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo
if (mode == Addressing_Constant || mode == Addressing_Invalid) {
expr->tav.value = value;
- } else if (mode == Addressing_Value && is_type_typeid(type)) {
+ } else if (mode == Addressing_Value && type != nullptr && is_type_typeid(type)) {
expr->tav.value = value;
- } else if (mode == Addressing_Value && is_type_proc(type)) {
+ } else if (mode == Addressing_Value && type != nullptr && is_type_proc(type)) {
expr->tav.value = value;
}
@@ -1637,6 +1807,26 @@ gb_internal bool add_entity_with_name(CheckerContext *c, Scope *scope, Ast *iden
}
return true;
}
+
+gb_internal bool add_entity_with_name(CheckerInfo *info, Scope *scope, Ast *identifier, Entity *entity, String name) {
+ if (scope == nullptr) {
+ return false;
+ }
+
+
+ if (!is_blank_ident(name)) {
+ Entity *ie = scope_insert(scope, entity);
+ if (ie != nullptr) {
+ return redeclaration_error(name, entity, ie);
+ }
+ }
+ if (identifier != nullptr) {
+ GB_ASSERT(entity->file != nullptr);
+ add_entity_definition(info, identifier, entity);
+ }
+ return true;
+}
+
gb_internal bool add_entity(CheckerContext *c, Scope *scope, Ast *identifier, Entity *entity) {
return add_entity_with_name(c, scope, identifier, entity, entity->token.string);
}
@@ -1649,13 +1839,14 @@ gb_internal void add_entity_use(CheckerContext *c, Ast *identifier, Entity *enti
entity->flags |= EntityFlag_Used;
if (entity_has_deferred_procedure(entity)) {
Entity *deferred = entity->Procedure.deferred_procedure.entity;
- add_entity_use(c, nullptr, deferred);
+ if (deferred != entity) {
+ add_entity_use(c, nullptr, deferred);
+ }
}
if (identifier == nullptr || identifier->kind != Ast_Ident) {
return;
}
- Ast *empty_ident = nullptr;
- entity->identifier.compare_exchange_strong(empty_ident, identifier);
+ entity->identifier.store(identifier);
identifier->Ident.entity = entity;
@@ -1818,8 +2009,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_dependency(c->info, c->decl, t);
MUTEX_GUARD_BLOCK(&c->info->type_info_mutex) {
- MapFindResult fr;
- auto found = map_try_get(&c->info->type_info_map, t, &fr);
+ auto found = map_get(&c->info->type_info_map, t);
if (found != nullptr) {
// Types have already been added
return;
@@ -1843,7 +2033,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
ti_index = c->info->type_info_types.count;
array_add(&c->info->type_info_types, t);
}
- map_set_internal_from_try_get(&c->checker->info.type_info_map, t, ti_index, fr);
+ map_set(&c->checker->info.type_info_map, t, ti_index);
if (prev) {
// NOTE(bill): If a previous one exists already, no need to continue
@@ -1961,6 +2151,8 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
break;
case Type_Struct:
+ if (bt->Struct.fields_wait_signal.futex.load() == 0)
+ return;
if (bt->Struct.scope != nullptr) {
for (auto const &entry : bt->Struct.scope->elements) {
Entity *e = entry.value;
@@ -1981,7 +2173,9 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i];
- add_type_info_type_internal(c, f->type);
+ if (f && f->type) {
+ add_type_info_type_internal(c, f->type);
+ }
}
add_comparison_procedures_for_fields(c, bt);
break;
@@ -2010,16 +2204,6 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->SimdVector.elem);
break;
- case Type_RelativePointer:
- add_type_info_type_internal(c, bt->RelativePointer.pointer_type);
- add_type_info_type_internal(c, bt->RelativePointer.base_integer);
- break;
-
- case Type_RelativeMultiPointer:
- add_type_info_type_internal(c, bt->RelativeMultiPointer.pointer_type);
- add_type_info_type_internal(c, bt->RelativeMultiPointer.base_integer);
- break;
-
case Type_Matrix:
add_type_info_type_internal(c, bt->Matrix.elem);
break;
@@ -2028,6 +2212,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->SoaPointer.elem);
break;
+ case Type_BitField:
+ add_type_info_type_internal(c, bt->BitField.backing_type);
+ for (Entity *f : bt->BitField.fields) {
+ add_type_info_type_internal(c, f->type);
+ }
+ break;
case Type_Generic:
break;
@@ -2104,7 +2294,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
// IMPORTANT NOTE(bill): this must be copied as `map_set` takes a const ref
// and effectively assigns the `+1` of the value
isize const count = set->count;
- if (map_set_if_not_previously_exists(set, ti_index, count)) {
+ if (map_set_if_not_previously_exists(set, ti_index+1, count)) {
// Type already exists;
return;
}
@@ -2211,9 +2401,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;
@@ -2254,16 +2449,6 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->SimdVector.elem);
break;
- case Type_RelativePointer:
- add_min_dep_type_info(c, bt->RelativePointer.pointer_type);
- add_min_dep_type_info(c, bt->RelativePointer.base_integer);
- break;
-
- case Type_RelativeMultiPointer:
- add_min_dep_type_info(c, bt->RelativeMultiPointer.pointer_type);
- add_min_dep_type_info(c, bt->RelativeMultiPointer.base_integer);
- break;
-
case Type_Matrix:
add_min_dep_type_info(c, bt->Matrix.elem);
break;
@@ -2272,6 +2457,13 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->SoaPointer.elem);
break;
+ case Type_BitField:
+ add_min_dep_type_info(c, bt->BitField.backing_type);
+ for (Entity *f : bt->BitField.fields) {
+ add_min_dep_type_info(c, f->type);
+ }
+ break;
+
default:
GB_PANIC("Unhandled type: %*.s", LIT(type_strings[bt->kind]));
break;
@@ -2289,7 +2481,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;
@@ -2340,99 +2531,45 @@ gb_internal void force_add_dependency_entity(Checker *c, Scope *scope, String co
add_dependency_to_set(c, e);
}
+gb_internal void collect_testing_procedures_of_package(Checker *c, AstPackage *pkg) {
+ AstPackage *testing_package = get_core_package(&c->info, str_lit("testing"));
+ Scope *testing_scope = testing_package->scope;
+ Entity *test_signature = scope_lookup_current(testing_scope, str_lit("Test_Signature"));
+ Scope *s = pkg->scope;
+ for (auto const &entry : s->elements) {
+ Entity *e = entry.value;
+ if (e->kind != Entity_Procedure) {
+ continue;
+ }
-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"),
+ if ((e->flags & EntityFlag_Test) == 0) {
+ continue;
+ }
- // Global variables
- str_lit("type_table"),
- str_lit("__type_info_of"),
- );
+ String name = e->token.string;
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point,
- // Global variables
- str_lit("args__"),
- );
+ bool is_tester = true;
- FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()),
- // NOTE(bill): Only if these exist
- str_lit("_tls_index"),
- str_lit("_fltused"),
- );
+ Type *t = base_type(e->type);
+ GB_ASSERT(t->kind == Type_Proc);
+ if (are_types_identical(t, base_type(test_signature->type))) {
+ // Good
+ } else {
+ gbString str = type_to_string(t);
+ error(e->token, "Testing procedures must have a signature type of proc(^testing.T), got %s", str);
+ gb_string_free(str);
+ is_tester = false;
+ }
- 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"),
- );
+ if (is_tester) {
+ add_dependency_to_set(c, e);
+ array_add(&c->info.testing_procedures, e);
+ }
+ }
+}
+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) {
@@ -2490,6 +2627,11 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
is_init = false;
}
+ if ((e->flags & EntityFlag_Disabled) != 0) {
+ warning(e->token, "This @(init) procedure is disabled; you must call it manually");
+ is_init = false;
+ }
+
if (is_init) {
add_dependency_to_set(c, e);
array_add(&c->info.init_procedures, e);
@@ -2534,47 +2676,126 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
}
}
+ AstPackage *pkg = c->info.init_package;
+ collect_testing_procedures_of_package(c, pkg);
- Entity *test_signature = scope_lookup_current(testing_scope, str_lit("Test_Signature"));
+ if (build_context.test_all_packages) {
+ for (auto const &entry : c->info.packages) {
+ AstPackage *pkg = entry.value;
+ collect_testing_procedures_of_package(c, pkg);
+ }
+ }
+ } else if (start != nullptr) {
+ 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
- AstPackage *pkg = c->info.init_package;
- Scope *s = pkg->scope;
+ ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap);
+ map_init(&c->info.minimum_dependency_type_info_set);
- for (auto const &entry : s->elements) {
- Entity *e = entry.value;
- if (e->kind != Entity_Procedure) {
- continue;
- }
+#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)
- if ((e->flags & EntityFlag_Test) == 0) {
- continue;
- }
+ // required runtime entities
+ FORCE_ADD_RUNTIME_ENTITIES(true,
+ // Odin types
+ str_lit("Source_Code_Location"),
+ str_lit("Context"),
+ str_lit("Allocator"),
+ str_lit("Logger"),
- String name = e->token.string;
+ // Odin internal procedures
+ str_lit("__init_context"),
+ // str_lit("cstring_to_string"),
+ str_lit("_cleanup_runtime"),
- bool is_tester = true;
+ // Pseudo-CRT required procedures
+ str_lit("memset"),
- Type *t = base_type(e->type);
- GB_ASSERT(t->kind == Type_Proc);
- if (are_types_identical(t, base_type(test_signature->type))) {
- // Good
- } else {
- gbString str = type_to_string(t);
- error(e->token, "Testing procedures must have a signature type of proc(^testing.T), got %s", str);
- gb_string_free(str);
- is_tester = false;
- }
+ // Utility procedures
+ str_lit("memory_equal"),
+ str_lit("memory_compare"),
+ str_lit("memory_compare_zero"),
+ );
+
+ // Only required if no CRT is present
+ FORCE_ADD_RUNTIME_ENTITIES(build_context.no_crt,
+ str_lit("memcpy"),
+ str_lit("memmove"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(build_context.metrics.arch == TargetArch_arm32,
+ str_lit("aeabi_d2h")
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(is_arch_wasm() && !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"),
+ str_lit("__lshrti3"),
+ );
+
+ 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);
- if (is_tester) {
- add_dependency_to_set(c, e);
- array_add(&c->info.testing_procedures, e);
- }
- }
- } else if (start != nullptr) {
- start->flags |= EntityFlag_Used;
- add_dependency_to_set(c, start);
- }
#undef FORCE_ADD_RUNTIME_ENTITIES
}
@@ -2806,6 +3027,11 @@ gb_internal void init_core_type_info(Checker *c) {
return;
}
Entity *type_info_entity = find_core_entity(c, str_lit("Type_Info"));
+ GB_ASSERT(type_info_entity != nullptr);
+ if (type_info_entity->type == nullptr) {
+ check_single_global_entity(c, type_info_entity, type_info_entity->decl_info);
+ }
+ GB_ASSERT(type_info_entity->type != nullptr);
t_type_info = type_info_entity->type;
t_type_info_ptr = alloc_type_pointer(t_type_info);
@@ -2847,10 +3073,9 @@ gb_internal void init_core_type_info(Checker *c) {
t_type_info_map = find_core_type(c, str_lit("Type_Info_Map"));
t_type_info_bit_set = find_core_type(c, str_lit("Type_Info_Bit_Set"));
t_type_info_simd_vector = find_core_type(c, str_lit("Type_Info_Simd_Vector"));
- t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer"));
- t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer"));
t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix"));
t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer"));
+ t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field"));
t_type_info_named_ptr = alloc_type_pointer(t_type_info_named);
t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer);
@@ -2876,10 +3101,9 @@ gb_internal void init_core_type_info(Checker *c) {
t_type_info_map_ptr = alloc_type_pointer(t_type_info_map);
t_type_info_bit_set_ptr = alloc_type_pointer(t_type_info_bit_set);
t_type_info_simd_vector_ptr = alloc_type_pointer(t_type_info_simd_vector);
- t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer);
- t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer);
t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix);
t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer);
+ t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field);
}
gb_internal void init_mem_allocator(Checker *c) {
@@ -2907,6 +3131,16 @@ gb_internal void init_core_source_code_location(Checker *c) {
t_source_code_location_ptr = alloc_type_pointer(t_source_code_location);
}
+gb_internal void init_core_load_directory_file(Checker *c) {
+ if (t_load_directory_file != nullptr) {
+ return;
+ }
+ t_load_directory_file = find_core_type(c, str_lit("Load_Directory_File"));
+ t_load_directory_file_ptr = alloc_type_pointer(t_load_directory_file);
+ t_load_directory_file_slice = alloc_type_slice(t_load_directory_file);
+}
+
+
gb_internal void init_core_map_type(Checker *c) {
if (t_map_info != nullptr) {
return;
@@ -2971,8 +3205,8 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
return true;
} else if (name == "link_prefix") {
if (ev.kind == ExactValue_String) {
- String link_prefix = ev.value_string;
- if (!is_foreign_name_valid(link_prefix)) {
+ String link_prefix = string_trim_whitespace(ev.value_string);
+ if (link_prefix.len != 0 && !is_foreign_name_valid(link_prefix)) {
error(elem, "Invalid link prefix: '%.*s'", LIT(link_prefix));
} else {
c->foreign_context.link_prefix = link_prefix;
@@ -2981,6 +3215,18 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
+ } else if (name == "link_suffix") {
+ if (ev.kind == ExactValue_String) {
+ String link_suffix = string_trim_whitespace(ev.value_string);
+ if (link_suffix.len != 0 && !is_foreign_name_valid(link_suffix)) {
+ error(elem, "Invalid link suffix: '%.*s'", LIT(link_suffix));
+ } else {
+ c->foreign_context.link_suffix = link_suffix;
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
} else if (name == "private") {
EntityVisiblityKind kind = EntityVisiblity_PrivateToPackage;
if (ev.kind == ExactValue_Invalid) {
@@ -2999,6 +3245,12 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
}
c->foreign_context.visibility_kind = kind;
return true;
+ } else if (name == "require_results") {
+ if (value != nullptr) {
+ error(elem, "Expected no value for '%.*s'", LIT(name));
+ }
+ c->foreign_context.require_results = true;
+ return true;
}
return false;
@@ -3095,6 +3347,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
linkage == "link_once") {
ac->linkage = linkage;
} else {
+ ERROR_BLOCK();
error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage));
error_line("\tinternal\n");
error_line("\tstrong\n");
@@ -3267,36 +3520,34 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
if (ev.kind == ExactValue_String) {
ac->link_prefix = ev.value_string;
- if (!is_foreign_name_valid(ac->link_prefix)) {
+ if (ac->link_prefix.len != 0 && !is_foreign_name_valid(ac->link_prefix)) {
error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
- } else if (name == "deprecated") {
+ } else if (name == "link_suffix") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
- String msg = ev.value_string;
- if (msg.len == 0) {
- error(elem, "Deprecation message cannot be an empty string");
- } else {
- ac->deprecated_message = msg;
+ ac->link_suffix = ev.value_string;
+ if (ac->link_suffix.len != 0 && !is_foreign_name_valid(ac->link_suffix)) {
+ error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
- } else if (name == "warning") {
+ } else if (name == "deprecated") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
String msg = ev.value_string;
if (msg.len == 0) {
- error(elem, "Warning message cannot be an empty string");
+ error(elem, "Deprecation message cannot be an empty string");
} else {
- ac->warning_message = msg;
+ ac->deprecated_message = msg;
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
@@ -3336,18 +3587,19 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
String mode = ev.value_string;
if (mode == "none") {
ac->optimization_mode = ProcedureOptimizationMode_None;
+ } else if (mode == "favor_size") {
+ ac->optimization_mode = ProcedureOptimizationMode_FavorSize;
} else if (mode == "minimal") {
- ac->optimization_mode = ProcedureOptimizationMode_Minimal;
+ error(elem, "Invalid optimization_mode 'minimal' for '%.*s', mode has been removed due to confusion, but 'none' has the same behaviour", LIT(name));
} else if (mode == "size") {
- ac->optimization_mode = ProcedureOptimizationMode_Size;
+ error(elem, "Invalid optimization_mode 'size' for '%.*s', mode has been removed due to confusion, but 'favor_size' has the same behaviour", LIT(name));
} else if (mode == "speed") {
- ac->optimization_mode = ProcedureOptimizationMode_Speed;
+ error(elem, "Invalid optimization_mode 'speed' for '%.*s', mode has been removed due to confusion, but 'favor_size' has the same behaviour", LIT(name));
} else {
+ ERROR_BLOCK();
error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name));
error_line("\tnone\n");
- error_line("\tminimal\n");
- error_line("\tsize\n");
- error_line("\tspeed\n");
+ error_line("\tfavor_size\n");
}
} else {
error(elem, "Expected a string for '%.*s'", LIT(name));
@@ -3405,6 +3657,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;
}
@@ -3422,6 +3707,12 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
}
ac->is_static = true;
return true;
+ } else if (name == "rodata") {
+ if (value != nullptr) {
+ error(elem, "'rodata' does not have any parameters");
+ }
+ ac->rodata = true;
+ return true;
} else if (name == "thread_local") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ac->init_expr_list_count > 0) {
@@ -3440,6 +3731,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
model == "localexec") {
ac->thread_local_model = model;
} else {
+ ERROR_BLOCK();
error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model));
error_line("\tdefault\n");
error_line("\tlocaldynamic\n");
@@ -3490,6 +3782,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
linkage == "link_once") {
ac->linkage = linkage;
} else {
+ ERROR_BLOCK();
error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage));
error_line("\tinternal\n");
error_line("\tstrong\n");
@@ -3512,13 +3805,24 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
ac->link_prefix = ev.value_string;
- if (!is_foreign_name_valid(ac->link_prefix)) {
+ if (ac->link_prefix.len != 0 && !is_foreign_name_valid(ac->link_prefix)) {
error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
+ } else if (name == "link_suffix") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ ac->link_suffix = ev.value_string;
+ if (ac->link_suffix.len != 0 && !is_foreign_name_valid(ac->link_suffix)) {
+ error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix));
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
} else if (name == "link_section") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
@@ -3544,6 +3848,16 @@ gb_internal DECL_ATTRIBUTE_PROC(const_decl_attribute) {
} else if (name == "private") {
// NOTE(bill): Handled elsewhere `check_collect_value_decl`
return true;
+ } else if (name == "static" ||
+ name == "thread_local" ||
+ name == "require" ||
+ name == "linkage" ||
+ name == "link_name" ||
+ name == "link_prefix" ||
+ name == "link_suffix" ||
+ false) {
+ error(elem, "@(%.*s) is not supported for compile time constant value declarations", LIT(name));
+ return true;
}
return false;
}
@@ -3583,8 +3897,10 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at
if (attributes.count == 0) return;
String original_link_prefix = {};
+ String original_link_suffix = {};
if (ac) {
original_link_prefix = ac->link_prefix;
+ original_link_suffix = ac->link_suffix;
}
StringSet set = {};
@@ -3643,9 +3959,11 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at
}
if (!proc(c, elem, name, value, ac)) {
- if (!build_context.ignore_unknown_attributes) {
+ if (!build_context.ignore_unknown_attributes &&
+ !string_set_exists(&build_context.custom_attributes, name)) {
+ ERROR_BLOCK();
error(elem, "Unknown attribute element name '%.*s'", LIT(name));
- error_line("\tDid you forget to use build flag '-ignore-unknown-attributes'?\n");
+ error_line("\tDid you forget to use the build flag '-ignore-unknown-attributes' or '-custom-attribute:%.*s'?\n", LIT(name));
}
}
}
@@ -3658,6 +3976,12 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at
ac->link_prefix.len = 0;
}
}
+ if (ac->link_suffix.text == original_link_suffix.text) {
+ if (ac->link_name.len > 0) {
+ ac->link_suffix.text = nullptr;
+ ac->link_suffix.len = 0;
+ }
+ }
}
}
@@ -3713,6 +4037,8 @@ gb_internal bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_
gb_string_free(str);
return false;
} else if (is_global) {
+ ERROR_BLOCK();
+
Ast *n = vd->values[rhs-1];
error(n, "Expected %td expressions on the right hand side, got %td", lhs, rhs);
error_line("Note: Global declarations do not allow for multi-valued expressions");
@@ -3764,6 +4090,7 @@ gb_internal void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array<
case Entity_ProcGroup:
case Entity_Procedure:
case Entity_TypeName:
+ case Entity_Constant:
// Okay
break;
default:
@@ -3832,6 +4159,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
bool is_test = false;
bool is_init = false;
bool is_fini = false;
+ bool is_priv = false;
for_array(i, vd->attributes) {
Ast *attr = vd->attributes[i];
@@ -3876,6 +4204,8 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
if (!success) {
error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
+ } else {
+ is_priv = true;
}
@@ -3897,6 +4227,11 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
}
+ if (is_priv && is_test) {
+ error(decl, "Attribute 'private' is not allowed on a test case");
+ return;
+ }
+
if (entity_visibility_kind == EntityVisiblity_Public &&
(c->scope->flags&ScopeFlag_File) &&
c->scope->file) {
@@ -3931,6 +4266,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
Entity *e = alloc_entity_variable(c->scope, name->Ident.token, nullptr);
e->identifier = name;
e->file = c->file;
+ e->Variable.is_global = true;
if (entity_visibility_kind != EntityVisiblity_Public) {
e->flags |= EntityFlag_NotExported;
@@ -3948,6 +4284,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
e->Variable.foreign_library_ident = fl;
e->Variable.link_prefix = c->foreign_context.link_prefix;
+ e->Variable.link_suffix = c->foreign_context.link_suffix;
}
Ast *init_expr = value;
@@ -4002,6 +4339,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
ast_node(pl, ProcLit, init);
e = alloc_entity_procedure(d->scope, token, nullptr, pl->tags);
+ d->foreign_require_results = c->foreign_context.require_results;
if (fl != nullptr) {
GB_ASSERT(fl->kind == Ast_Ident);
e->Procedure.foreign_library_ident = fl;
@@ -4014,15 +4352,15 @@ 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;
+ e->Procedure.link_suffix = c->foreign_context.link_suffix;
GB_ASSERT(cc != ProcCC_Invalid);
pl->type->ProcType.calling_convention = cc;
@@ -4066,8 +4404,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]));
@@ -4128,17 +4465,22 @@ gb_internal bool correct_single_type_alias(CheckerContext *c, Entity *e) {
gb_internal bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) {
bool correction = false;
- u32 n = s->elements.count;
- for (u32 i = n-1; i < n; i--) {
- correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ for (u32 n = s->elements.count, i = n-1; i < n; i--) {
+ auto const &entry = s->elements.entries[i];
+ Entity *e = entry.value;
+ if (entry.hash && e != nullptr) {
+ correction |= correct_single_type_alias(c, e);
+ }
}
return correction;
}
gb_internal bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) {
bool correction = false;
- u32 n = s->elements.count;
- for (isize i = 0; i < n; i++) {
- correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ for (auto const &entry : s->elements) {
+ Entity *e = entry.value;
+ if (e != nullptr) {
+ correction |= correct_single_type_alias(c, entry.value);
+ }
}
return correction;
}
@@ -4221,7 +4563,9 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
case_end;
case_ast_node(fb, ForeignBlockDecl, decl);
- check_add_foreign_block_decl(c, decl);
+ if (curr_file != nullptr) {
+ array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl);
+ }
case_end;
default:
@@ -4237,6 +4581,14 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
// declared after this stmt in source
if (curr_file == nullptr) {
+ // For 'foreign' block statements that are not in file scope.
+ for_array(decl_index, nodes) {
+ Ast *decl = nodes[decl_index];
+ if (decl->kind == Ast_ForeignBlockDecl) {
+ check_add_foreign_block_decl(c, decl);
+ }
+ }
+
for_array(decl_index, nodes) {
Ast *decl = nodes[decl_index];
if (decl->kind == Ast_WhenStmt) {
@@ -4286,6 +4638,8 @@ gb_internal void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d)
}
gb_internal void check_all_global_entities(Checker *c) {
+ in_single_threaded_checker_stage = true;
+
// NOTE(bill): This must be single threaded
// Don't bother trying
for_array(i, c->info.entities) {
@@ -4297,10 +4651,16 @@ gb_internal void check_all_global_entities(Checker *c) {
DeclInfo *d = e->decl_info;
check_single_global_entity(c, e, d);
if (e->type != nullptr && is_type_typed(e->type)) {
+ for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) {
+ complete_soa_type(c, t, false);
+ }
+
(void)type_size_of(e->type);
(void)type_align_of(e->type);
}
}
+
+ in_single_threaded_checker_stage = false;
}
@@ -4387,7 +4747,7 @@ gb_internal void add_import_dependency_node(Checker *c, Ast *decl, PtrMap<AstPac
if (found == nullptr) {
Token token = ast_token(decl);
error(token, "Unable to find package: %.*s", LIT(path));
- gb_exit(1);
+ exit_with_errors();
}
AstPackage *pkg = *found;
GB_ASSERT(pkg->scope != nullptr);
@@ -4508,10 +4868,10 @@ gb_internal Array<ImportPathItem> find_import_path(Checker *c, AstPackage *start
continue;
}
- if (pkg->kind == Package_Runtime) {
- // NOTE(bill): Allow cyclic imports within the runtime package for the time being
- continue;
- }
+ // if (pkg->kind == Package_Runtime) {
+ // // NOTE(bill): Allow cyclic imports within the runtime package for the time being
+ // continue;
+ // }
ImportPathItem item = {pkg, decl};
if (pkg == end) {
@@ -4615,12 +4975,18 @@ gb_internal void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
}
- if (import_name.len == 0) {
+ if (is_blank_ident(import_name) && !is_blank_ident(id->import_name.string)) {
String invalid_name = id->fullpath;
invalid_name = get_invalid_import_name(invalid_name);
- error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import <new_name> \"%.*s\" ", LIT(invalid_name), LIT(invalid_name));
- error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
+ ERROR_BLOCK();
+
+ if (id->import_name.string.len > 0) {
+ error(token, "Import name '%.*s' cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
+ } else {
+ error(id->token, "Import name '%.*s' is not a valid identifier", LIT(invalid_name));
+ error_line("\tSuggestion: Rename the directory or explicitly set an import name like this 'import <new_name> %.*s'", LIT(id->relpath.string));
+ }
} else {
GB_ASSERT(id->import_name.pos.line != 0);
id->import_name.string = import_name;
@@ -4672,6 +5038,114 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
return false;
}
+gb_internal void check_foreign_import_fullpaths(Checker *c) {
+ CheckerContext ctx = make_checker_context(c);
+
+ UntypedExprInfoMap untyped = {};
+ defer (map_destroy(&untyped));
+
+ for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_imports_to_check_fullpaths, &e); /**/) {
+ GB_ASSERT(e != nullptr);
+ GB_ASSERT(e->kind == Entity_LibraryName);
+ Ast *decl = e->LibraryName.decl;
+ ast_node(fl, ForeignImportDecl, decl);
+
+ AstFile *f = decl->file();
+
+ reset_checker_context(&ctx, f, &untyped);
+ ctx.collect_delayed_decls = false;
+
+ GB_ASSERT(ctx.scope == e->scope);
+
+ if (fl->fullpaths.count == 0) {
+ String base_dir = dir_from_path(decl->file()->fullpath);
+
+ auto fullpaths = array_make<String>(permanent_allocator(), 0, fl->filepaths.count);
+
+ for (Ast *fp_node : fl->filepaths) {
+ Operand op = {};
+ check_expr(&ctx, &op, fp_node);
+ if (op.mode != Addressing_Constant && op.value.kind != ExactValue_String) {
+ gbString s = expr_to_string(op.expr);
+ error(fp_node, "Expected a constant string value, got '%s'", s);
+ gb_string_free(s);
+ continue;
+ }
+ if (!is_type_string(op.type)) {
+ gbString s = type_to_string(op.type);
+ error(fp_node, "Expected a constant string value, got value of type '%s'", s);
+ gb_string_free(s);
+ continue;
+ }
+
+ String file_str = op.value.value_string;
+ file_str = string_trim_whitespace(file_str);
+ String fullpath = file_str;
+ if (!is_arch_wasm() || string_ends_with(file_str, str_lit(".o"))) {
+ String foreign_path = {};
+ bool ok = determine_path_from_string(nullptr, decl, base_dir, file_str, &foreign_path, /*use error not syntax_error*/true);
+ if (ok) {
+ fullpath = foreign_path;
+ }
+ }
+ array_add(&fullpaths, fullpath);
+ }
+ fl->fullpaths = slice_from_array(fullpaths);
+ }
+
+ for (String const &path : fl->fullpaths) {
+ String ext = path_extension(path);
+ if (str_eq_ignore_case(ext, ".c") ||
+ str_eq_ignore_case(ext, ".cpp") ||
+ str_eq_ignore_case(ext, ".cxx") ||
+ str_eq_ignore_case(ext, ".h") ||
+ str_eq_ignore_case(ext, ".hpp") ||
+ str_eq_ignore_case(ext, ".hxx") ||
+ false
+ ) {
+ error(fl->token, "With 'foreign import', you cannot import a %.*s file/directory, you must precompile the library and link against that", LIT(ext));
+ break;
+ }
+ }
+
+ add_untyped_expressions(ctx.info, &untyped);
+
+ e->LibraryName.paths = fl->fullpaths;
+ }
+
+ for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_decls_to_check, &e); /**/) {
+ GB_ASSERT(e != nullptr);
+ if (e->kind != Entity_Procedure) {
+ continue;
+ }
+ if (!is_arch_wasm()) {
+ continue;
+ }
+ Entity *foreign_library = e->Procedure.foreign_library;
+ GB_ASSERT(foreign_library != nullptr);
+
+ String name = e->Procedure.link_name;
+
+ String module_name = str_lit("env");
+ GB_ASSERT (foreign_library->kind == Entity_LibraryName);
+ if (foreign_library->LibraryName.paths.count != 1) {
+ error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
+ LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
+ }
+
+ if (foreign_library->LibraryName.paths.count >= 1) {
+ module_name = foreign_library->LibraryName.paths[0];
+ }
+
+ if (!string_ends_with(module_name, str_lit(".o"))) {
+ name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
+ }
+ e->Procedure.link_name = name;
+
+ check_foreign_procedure(&ctx, e, e->decl_info);
+ }
+}
+
gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
if (decl->state_flags & StateFlag_BeenHandled) return;
decl->state_flags |= StateFlag_BeenHandled;
@@ -4681,43 +5155,26 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
Scope *parent_scope = ctx->scope;
GB_ASSERT(parent_scope->flags&ScopeFlag_File);
- GB_ASSERT(fl->fullpaths.count > 0);
- String fullpath = fl->fullpaths[0];
- String library_name = path_to_entity_name(fl->library_name.string, fullpath);
- if (is_blank_ident(library_name)) {
- error(fl->token, "File name, %.*s, cannot be as a library name as it is not a valid identifier", LIT(fl->library_name.string));
+ String library_name = fl->library_name.string;
+ if (library_name.len == 0 && fl->fullpaths.count != 0) {
+ String fullpath = fl->fullpaths[0];
+ library_name = path_to_entity_name(fl->library_name.string, fullpath);
+ }
+ if (library_name.len == 0 || is_blank_ident(library_name)) {
+ error(fl->token, "File name, '%.*s', cannot be as a library name as it is not a valid identifier", LIT(library_name));
return;
}
- // if (fl->collection_name != "system") {
- // char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1);
- // defer (gb_free(heap_allocator(), c_str));
- // gb_memmove(c_str, fullpath.text, fullpath.len);
- // c_str[fullpath.len] = '\0';
-
- // gbFile f = {};
- // gbFileError file_err = gb_file_open(&f, c_str);
- // defer (gb_file_close(&f));
-
- // switch (file_err) {
- // case gbFileError_Invalid:
- // error(decl, "Invalid file or cannot be found ('%.*s')", LIT(fullpath));
- // return;
- // case gbFileError_NotExists:
- // error(decl, "File cannot be found ('%.*s')", LIT(fullpath));
- // return;
- // }
- // }
GB_ASSERT(fl->library_name.pos.line != 0);
fl->library_name.string = library_name;
Entity *e = alloc_entity_library_name(parent_scope, fl->library_name, t_invalid,
fl->fullpaths, library_name);
+ e->LibraryName.decl = decl;
add_entity_flags_from_file(ctx, e, parent_scope);
add_entity(ctx, parent_scope, nullptr, e);
-
AttributeContext ac = {};
check_decl_attributes(ctx, fl->attributes, foreign_import_decl_attribute, &ac);
if (ac.require_declaration) {
@@ -4732,12 +5189,8 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
e->LibraryName.extra_linker_flags = extra_linker_flags;
}
- if (has_asm_extension(fullpath)) {
- if (build_context.metrics.arch != TargetArch_amd64) {
- 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]));
- }
- }
+ mpsc_enqueue(&ctx->info->foreign_imports_to_check_fullpaths, e);
+
}
// Returns true if a new package is present
@@ -4844,9 +5297,9 @@ gb_internal bool collect_file_decl(CheckerContext *ctx, Ast *decl) {
case_end;
case_ast_node(fb, ForeignBlockDecl, decl);
- if (check_add_foreign_block_decl(ctx, decl)) {
- return true;
- }
+ GB_ASSERT(ctx->collect_delayed_decls);
+ decl->state_flags |= StateFlag_BeenHandled;
+ array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl);
case_end;
case_ast_node(ws, WhenStmt, decl);
@@ -4911,7 +5364,7 @@ gb_internal void check_create_file_scopes(Checker *c) {
for_array(i, c->parser->packages) {
AstPackage *pkg = c->parser->packages[i];
- gb_sort_array(pkg->files.data, pkg->files.count, sort_file_by_name);
+ array_sort(pkg->files, sort_file_by_name);
isize total_pkg_decl_count = 0;
for_array(j, pkg->files) {
@@ -5129,8 +5582,6 @@ gb_internal void check_import_entities(Checker *c) {
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
- ctx.collect_delayed_decls = false;
-
correct_type_aliases_in_scope(&ctx, pkg->scope);
}
@@ -5138,6 +5589,17 @@ gb_internal void check_import_entities(Checker *c) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
+ ctx.collect_delayed_decls = true;
+ for (Ast *decl : f->delayed_decls_queues[AstDelayQueue_ForeignBlock]) {
+ check_add_foreign_block_decl(&ctx, decl);
+ }
+ array_clear(&f->delayed_decls_queues[AstDelayQueue_ForeignBlock]);
+ }
+
+ for_array(i, pkg->files) {
+ AstFile *f = pkg->files[i];
+ reset_checker_context(&ctx, f, &untyped);
+
for (Ast *expr : f->delayed_decls_queues[AstDelayQueue_Expr]) {
Operand o = {};
check_expr(&ctx, &o, expr);
@@ -5304,6 +5766,59 @@ 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;
+ }
+ if ((e->flags & EntityFlag_Overridden) != 0) {
+ // NOTE (zen3ger) Delay checking of a proc alias until the underlying proc is checked.
+ GB_ASSERT(e->aliased_of != nullptr);
+ GB_ASSERT(e->aliased_of->kind == Entity_Procedure);
+ if ((e->aliased_of->flags & EntityFlag_ProcBodyChecked) != 0) {
+ e->flags |= EntityFlag_ProcBodyChecked;
+ return;
+ }
+ // NOTE (zen3ger) A proc alias *does not* have a body and tags!
+ check_procedure_later(c, e->file, e->token, e->decl_info, e->type, nullptr, 0);
+ return;
+ }
+ Type *type = base_type(e->type);
+ if (type == t_invalid) {
+ return;
+ }
+ GB_ASSERT_MSG(type->kind == Type_Proc, "%s", type_to_string(e->type));
+
+ 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 +5925,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 +5956,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,35 +5998,27 @@ gb_internal void check_safety_all_procedures_for_unchecked(Checker *c) {
}
}
-gb_internal void check_test_procedures(Checker *c) {
- if (build_context.test_names.entries.count == 0) {
- return;
- }
+gb_internal GB_COMPARE_PROC(init_procedures_cmp);
+gb_internal GB_COMPARE_PROC(fini_procedures_cmp);
- AstPackage *pkg = c->info.init_package;
- Scope *s = pkg->scope;
+gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Array<Entity *> *array) {
+ Entity *prev = nullptr;
- for (String const &name : build_context.test_names) {
- Entity *e = scope_lookup(s, name);
- if (e == nullptr) {
- Token tok = {};
- if (pkg->files.count != 0) {
- tok = pkg->files[0]->tokens[0];
- }
- error(tok, "Unable to find the test '%.*s' in 'package %.*s' ", LIT(name), LIT(pkg->name));
- }
- }
-
- for (isize i = 0; i < c->info.testing_procedures.count; /**/) {
- Entity *e = c->info.testing_procedures[i];
- String name = e->token.string;
- if (!string_set_exists(&build_context.test_names, name)) {
- array_ordered_remove(&c->info.testing_procedures, i);
+ 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) {
+ array_sort(c->info.testing_procedures, init_procedures_cmp);
+ remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures);
}
@@ -5690,6 +6183,11 @@ gb_internal void check_deferred_procedures(Checker *c) {
case DeferredProcedure_in_out_by_ptr: attribute = "deferred_in_out_by_ptr"; break;
}
+ if (src == dst) {
+ error(src->token, "'%.*s' cannot be used as its own %s", LIT(dst->token.string), attribute);
+ continue;
+ }
+
if (is_type_polymorphic(src->type) || is_type_polymorphic(dst->type)) {
error(src->token, "'%s' cannot be used with a polymorphic procedure", attribute);
continue;
@@ -5737,7 +6235,7 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
if ((src_params == nullptr && dst_params != nullptr) ||
(src_params != nullptr && dst_params == nullptr)) {
- error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string));
+ error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s'", LIT(dst->token.string), LIT(src->token.string));
continue;
}
@@ -5750,8 +6248,8 @@ gb_internal void check_deferred_procedures(Checker *c) {
gbString s = type_to_string(src_params);
gbString d = type_to_string(dst_params);
error(src->token, "Deferred procedure '%.*s' parameters do not match the inputs of initial procedure '%.*s':\n\t(%s) =/= (%s)",
- LIT(src->token.string), LIT(dst->token.string),
- s, d
+ LIT(dst->token.string), LIT(src->token.string),
+ d, s
);
gb_string_free(d);
gb_string_free(s);
@@ -5767,7 +6265,7 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
if ((src_results == nullptr && dst_params != nullptr) ||
(src_results != nullptr && dst_params == nullptr)) {
- error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(src->token.string), LIT(dst->token.string));
+ error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s'", LIT(dst->token.string), LIT(src->token.string));
continue;
}
@@ -5780,8 +6278,8 @@ gb_internal void check_deferred_procedures(Checker *c) {
gbString s = type_to_string(src_results);
gbString d = type_to_string(dst_params);
error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)",
- LIT(src->token.string), LIT(dst->token.string),
- s, d
+ LIT(dst->token.string), LIT(src->token.string),
+ d, s
);
gb_string_free(d);
gb_string_free(s);
@@ -5833,8 +6331,8 @@ gb_internal void check_deferred_procedures(Checker *c) {
gbString s = type_to_string(tsrc);
gbString d = type_to_string(dst_params);
error(src->token, "Deferred procedure '%.*s' parameters do not match the results of initial procedure '%.*s':\n\t(%s) =/= (%s)",
- LIT(src->token.string), LIT(dst->token.string),
- s, d
+ LIT(dst->token.string), LIT(src->token.string),
+ d, s
);
gb_string_free(d);
gb_string_free(s);
@@ -5873,11 +6371,14 @@ gb_internal void check_unique_package_names(Checker *c) {
continue;
}
+
+ begin_error_block();
error(curr, "Duplicate declaration of 'package %.*s'", LIT(name));
error_line("\tA package name must be unique\n"
"\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
"\tA package name is required for link name prefixing to have a consistent ABI\n");
- error(prev, "found at previous location");
+ error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos));
+ end_error_block();
}
}
@@ -5898,6 +6399,9 @@ gb_internal void check_add_definitions_from_queues(Checker *c) {
}
gb_internal void check_merge_queues_into_arrays(Checker *c) {
+ for (Type *t = nullptr; mpsc_dequeue(&c->soa_types_to_complete, &t); /**/) {
+ complete_soa_type(c, t, false);
+ }
check_add_entities_from_queues(c);
check_add_definitions_from_queues(c);
}
@@ -5943,10 +6447,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);
+ array_sort(c->info.init_procedures, init_procedures_cmp);
+ array_sort(c->info.fini_procedures, 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) {
@@ -6041,18 +6549,22 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check procedure bodies");
check_procedure_bodies(c);
+ TIME_SECTION("check foreign import fullpaths");
+ check_foreign_import_fullpaths(c);
+
TIME_SECTION("add entities from procedure bodies");
check_merge_queues_into_arrays(c);
TIME_SECTION("check scope usage");
for (auto const &entry : c->info.files) {
AstFile *f = entry.value;
- u64 vet_flags = build_context.vet_flags;
- if (f->vet_flags_set) {
- vet_flags = f->vet_flags;
- }
+ u64 vet_flags = ast_file_vet_flags(f);
check_scope_usage(c, f->scope, vet_flags);
}
+ for (auto const &entry : c->info.packages) {
+ AstPackage *pkg = entry.value;
+ check_scope_usage_internal(c, pkg->scope, 0, true);
+ }
TIME_SECTION("add basic type information");
// Add "Basic" type information
@@ -6091,9 +6603,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);
@@ -6107,8 +6616,21 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check bodies have all been checked");
check_unchecked_bodies(c);
+ TIME_SECTION("check #soa types");
+
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) {
@@ -6130,6 +6652,8 @@ gb_internal void check_parsed_files(Checker *c) {
error(token, "Undefined entry point procedure 'main'");
}
+ } else if (build_context.build_mode == BuildMode_DynamicLibrary && build_context.no_entry_point) {
+ c->info.entry_point = nullptr;
}
thread_pool_wait();
@@ -6150,6 +6674,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); /**/) {