mirror of
https://github.com/odin-lang/Odin.git
synced 2026-10-08 22:01:47 -04:00
Make global checking state thread-safe: read-only global scopes, per-group untyped and #soa, deferred link-name checks, fresh contexts for on-demand globals
This commit is contained in:
1 parent
94bb955871
commit
73c3a96de1
10 files changed
+396
-201
No files matched your search
+127
-77
@@ -170,7 +170,7 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e
|
||||
// has been "evaluated" and the variant data can be copied across
|
||||
|
||||
rw_mutex_lock(&found_scope->mutex);
|
||||
scope_map_insert(&found_scope->elements, original_intern, hash, new_entity);
|
||||
scope_map_replace(&found_scope->elements, original_intern, hash, new_entity);
|
||||
rw_mutex_unlock(&found_scope->mutex);
|
||||
|
||||
original_entity->flags |= EntityFlag_Overridden;
|
||||
@@ -1257,41 +1257,95 @@ gb_internal void check_target_feature_attributes(AttributeContext &ac, Entity *e
|
||||
}
|
||||
}
|
||||
|
||||
gb_internal void check_foreign_procedure(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
GB_ASSERT(e != nullptr);
|
||||
GB_ASSERT(e->kind == Entity_Procedure);
|
||||
String name = e->Procedure.link_name;
|
||||
gb_internal void add_link_name_use(CheckerInfo *info, String name, Entity *e, DeclInfo *d, LinkNameUseKind kind) {
|
||||
mutex_lock(&info->foreign_mutex);
|
||||
array_add(&info->link_names, LinkNameUse{name, e, d, kind});
|
||||
mutex_unlock(&info->foreign_mutex);
|
||||
}
|
||||
|
||||
mutex_lock(&ctx->info->foreign_mutex);
|
||||
gb_internal GB_COMPARE_PROC(link_name_use_cmp) {
|
||||
LinkNameUse const *x = cast(LinkNameUse const *)a;
|
||||
LinkNameUse const *y = cast(LinkNameUse const *)b;
|
||||
i32 cmp = string_compare(x->name, y->name);
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
if (x->entity != y->entity) {
|
||||
return entity_source_order_cmp(x->entity, y->entity);
|
||||
}
|
||||
return i32_cmp(x->kind, y->kind);
|
||||
}
|
||||
gb_internal void check_link_name_uses(Checker *c) {
|
||||
auto &uses = c->info.link_names;
|
||||
array_sort(uses, link_name_use_cmp);
|
||||
|
||||
LinkNameUse *first = nullptr;
|
||||
for (isize i = 0; i < uses.count; i++) {
|
||||
LinkNameUse *u = &uses[i];
|
||||
if (i > 0 && uses[i-1].name != u->name) {
|
||||
first = nullptr;
|
||||
} else if (i > 0 && uses[i-1].entity == u->entity) {
|
||||
continue;
|
||||
}
|
||||
String name = u->name;
|
||||
if (name == "main" && u->kind != LinkNameUse_Variable) {
|
||||
if (u->kind == LinkNameUse_ForeignProcedure || u->entity->pkg->kind != Package_Runtime) {
|
||||
error(u->decl->proc_lit, "The link name 'main' is reserved for internal use");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (first == nullptr) {
|
||||
first = u;
|
||||
continue;
|
||||
}
|
||||
|
||||
Entity *e = u->entity;
|
||||
Entity *f = first->entity;
|
||||
|
||||
auto *fp = &ctx->info->foreigns;
|
||||
StringHashKey key = string_hash_string(name);
|
||||
Entity **found = string_map_get(fp, key);
|
||||
if (found && e != *found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
|
||||
Type *this_type = base_type(e->type);
|
||||
Type *other_type = base_type(f->type);
|
||||
if (is_type_proc(this_type) && is_type_proc(other_type)) {
|
||||
if (!are_signatures_similar_enough(this_type, other_type)) {
|
||||
error(d->proc_lit,
|
||||
"Redeclaration of foreign procedure '%.*s' with different type signatures\n"
|
||||
|
||||
switch (u->kind) {
|
||||
case LinkNameUse_ForeignProcedure:
|
||||
if (is_type_proc(this_type) && is_type_proc(other_type)) {
|
||||
if (!are_signatures_similar_enough(this_type, other_type)) {
|
||||
error(u->decl->proc_lit,
|
||||
"Redeclaration of foreign procedure '%.*s' with different type signatures\n"
|
||||
"\tat %s",
|
||||
LIT(name), token_pos_to_string(pos));
|
||||
}
|
||||
} else if (!signature_parameter_similar_enough(this_type, other_type)) {
|
||||
error(u->decl->proc_lit,
|
||||
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
|
||||
"\tat %s",
|
||||
LIT(name), token_pos_to_string(pos));
|
||||
}
|
||||
} else if (!signature_parameter_similar_enough(this_type, other_type)) {
|
||||
error(d->proc_lit,
|
||||
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
|
||||
"\tat %s",
|
||||
break;
|
||||
case LinkNameUse_Procedure:
|
||||
// TODO(bill): Better error message?
|
||||
error(u->decl->proc_lit,
|
||||
"Non-unique linking name for procedure '%.*s'\n"
|
||||
"\tother at %s",
|
||||
LIT(name), token_pos_to_string(pos));
|
||||
break;
|
||||
case LinkNameUse_Variable:
|
||||
if (e->type == nullptr || f->type == nullptr || !signature_parameter_similar_enough(this_type, other_type)) {
|
||||
error(e->token,
|
||||
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
|
||||
"\tat %s",
|
||||
LIT(name), token_pos_to_string(pos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (name == "main") {
|
||||
error(d->proc_lit, "The link name 'main' is reserved for internal use");
|
||||
} else {
|
||||
string_map_set(fp, key, e);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&ctx->info->foreign_mutex);
|
||||
gb_internal void check_foreign_procedure(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
GB_ASSERT(e != nullptr);
|
||||
GB_ASSERT(e->kind == Entity_Procedure);
|
||||
add_link_name_use(ctx->info, e->Procedure.link_name, e, d, LinkNameUse_ForeignProcedure);
|
||||
}
|
||||
|
||||
gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
@@ -1562,14 +1616,17 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
}
|
||||
|
||||
if (e->pkg->kind == Package_Init) {
|
||||
if (ctx->info->entry_point != nullptr) {
|
||||
mutex_lock(&ctx->info->entry_point_mutex);
|
||||
Entity *prev_entry_point = ctx->info->entry_point;
|
||||
if (prev_entry_point == nullptr) {
|
||||
ctx->info->entry_point = e;
|
||||
}
|
||||
mutex_unlock(&ctx->info->entry_point_mutex);
|
||||
if (prev_entry_point != nullptr) {
|
||||
begin_error_block();
|
||||
error(e->token, "Redeclaration of the entry point procedure 'main'");
|
||||
error_line("\tSuggestion: Is this a single-file package? If so, try compiling using the `-file` flag.\n");
|
||||
end_error_block();
|
||||
|
||||
} else {
|
||||
ctx->info->entry_point = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1660,28 +1717,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
name = e->Procedure.link_name;
|
||||
}
|
||||
if (e->Procedure.link_name.len > 0 || is_export) {
|
||||
mutex_lock(&ctx->info->foreign_mutex);
|
||||
|
||||
auto *fp = &ctx->info->foreigns;
|
||||
StringHashKey key = string_hash_string(name);
|
||||
Entity **found = string_map_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
// TODO(bill): Better error message?
|
||||
error(d->proc_lit,
|
||||
"Non unique linking name for procedure '%.*s'\n"
|
||||
"\tother at %s",
|
||||
LIT(name), token_pos_to_string(pos));
|
||||
} else if (name == "main") {
|
||||
if (d->entity.load()->pkg->kind != Package_Runtime) {
|
||||
error(d->proc_lit, "The link name 'main' is reserved for internal use");
|
||||
}
|
||||
} else {
|
||||
string_map_set(fp, key, e);
|
||||
}
|
||||
|
||||
mutex_unlock(&ctx->info->foreign_mutex);
|
||||
add_link_name_use(ctx->info, name, e, d, LinkNameUse_Procedure);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1777,24 +1813,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast
|
||||
name = e->Variable.link_name;
|
||||
}
|
||||
|
||||
auto *fp = &ctx->info->foreigns;
|
||||
StringHashKey key = string_hash_string(name);
|
||||
Entity **found = string_map_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
Type *this_type = base_type(e->type);
|
||||
Type *other_type = base_type(f->type);
|
||||
bool type_is_null = (e->type == nullptr || f->type == nullptr);
|
||||
if (type_is_null || !signature_parameter_similar_enough(this_type, other_type)) {
|
||||
error(e->token,
|
||||
"Foreign entity '%.*s' previously declared elsewhere with a different type\n"
|
||||
"\tat %s",
|
||||
LIT(name), token_pos_to_string(pos));
|
||||
}
|
||||
} else {
|
||||
string_map_set(fp, key, e);
|
||||
}
|
||||
add_link_name_use(ctx->info, name, e, decl, LinkNameUse_Variable);
|
||||
}
|
||||
|
||||
if (e->Variable.link_name.len > 0) {
|
||||
@@ -1910,9 +1929,15 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
|
||||
|
||||
ptr_set_destroy(&entity_set);
|
||||
|
||||
// NOTE(bill, 2026-10-01): an invalid overload is dropped from this group only, as others may be using that procedure
|
||||
auto invalid = array_make<bool>(temporary_allocator(), pge->entities.count);
|
||||
for (isize j = 0; j < pge->entities.count; j++) {
|
||||
invalid[j] = false;
|
||||
}
|
||||
|
||||
for (isize j = 0; j < pge->entities.count; j++) {
|
||||
Entity *p = pge->entities[j];
|
||||
if (p->type == t_invalid) {
|
||||
if (p->type == t_invalid || invalid[j]) {
|
||||
// NOTE(bill): This invalid overload has already been handled
|
||||
continue;
|
||||
}
|
||||
@@ -1931,7 +1956,7 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
|
||||
|
||||
TokenPos pos = q->token.pos;
|
||||
|
||||
if (q->type == nullptr || q->type == t_invalid) {
|
||||
if (q->type == nullptr || q->type == t_invalid || invalid[k]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1987,10 +2012,17 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D
|
||||
|
||||
if (is_invalid) {
|
||||
error_line("\tprevious procedure at %s\n", token_pos_to_string(pos));
|
||||
q->type = t_invalid;
|
||||
invalid[k] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
isize valid_count = 0;
|
||||
for (isize j = 0; j < pge->entities.count; j++) {
|
||||
if (!invalid[j]) {
|
||||
pge->entities[valid_count++] = pge->entities[j];
|
||||
}
|
||||
}
|
||||
pge->entities.count = valid_count;
|
||||
|
||||
AttributeContext ac = {};
|
||||
check_decl_attributes(ctx, d->attributes, proc_group_attribute, &ac);
|
||||
@@ -2153,6 +2185,7 @@ gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d,
|
||||
mutex_unlock(&ctx->info->lazy_mutex);
|
||||
return;
|
||||
}
|
||||
lazy_mutex_depth += 1;
|
||||
}
|
||||
GlobalEntityTimingFrame timing_frame = global_entity_timing_begin(e);
|
||||
|
||||
@@ -2174,18 +2207,34 @@ gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d,
|
||||
}
|
||||
|
||||
CheckerContext c = *ctx;
|
||||
if (d->scope->flags & ScopeFlag_File) {
|
||||
// NOTE(bill): a global is checked in a context of its own file, never in that of whatever needed it first,
|
||||
// which may be in another file or package, or a procedure body.
|
||||
// Only the cycle detection carries over.
|
||||
CheckerTypePath *type_path = c.type_path;
|
||||
UntypedExprInfoMap *untyped = c.untyped;
|
||||
gb_zero_size(&c.pkg, gb_size_of(CheckerContext) - gb_offset_of(CheckerContext, pkg));
|
||||
add_curr_ast_file(&c, d->scope->file);
|
||||
c.type_path = type_path;
|
||||
c.untyped = untyped;
|
||||
}
|
||||
c.scope = d->scope;
|
||||
c.decl = d;
|
||||
c.type_level = 0;
|
||||
c.curr_proc_calling_convention = ProcCC_Contextless;
|
||||
|
||||
// NOTE: a file scope's is set from its own file and shared by every thread, see `create_scope_from_file`
|
||||
bool set_context = (c.scope->flags & ScopeFlag_File) == 0;
|
||||
auto prev_flags = c.scope->flags;
|
||||
defer (c.scope->flags = prev_flags);
|
||||
|
||||
if (check_feature_flags(ctx, d->decl_node) & OptInFeatureFlag_GlobalContext) {
|
||||
c.scope->flags |= ScopeFlag_ContextDefined;
|
||||
} else {
|
||||
c.scope->flags &= ~ScopeFlag_ContextDefined;
|
||||
defer (if (set_context) {
|
||||
c.scope->flags = prev_flags;
|
||||
});
|
||||
if (set_context) {
|
||||
if (check_feature_flags(ctx, d->decl_node) & OptInFeatureFlag_GlobalContext) {
|
||||
c.scope->flags |= ScopeFlag_ContextDefined;
|
||||
} else {
|
||||
c.scope->flags &= ~ScopeFlag_ContextDefined;
|
||||
}
|
||||
}
|
||||
|
||||
global_group_check_edge(ctx, e);
|
||||
@@ -2242,6 +2291,7 @@ end:;
|
||||
// NOTE(bill): Add it to the list of checked entities
|
||||
if (is_lazy) {
|
||||
array_add(&ctx->info->entities, e);
|
||||
lazy_mutex_depth -= 1;
|
||||
mutex_unlock(&ctx->info->lazy_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user