aboutsummaryrefslogtreecommitdiff
path: root/src/check_decl.cpp
diff options
context:
space:
mode:
authorMichael Lee <leecommamichael@gmail.com>2025-12-23 16:12:53 -0600
committerGitHub <noreply@github.com>2025-12-23 16:12:53 -0600
commit550e57aba977ff766e5ab38a4c13a8dc18d55992 (patch)
tree6704ea53d838f7d7427e5bf6faa1d586378869b3 /src/check_decl.cpp
parent729d0a8e8ace759d5d1358b14b20e19f68f44ff0 (diff)
parent57352d9933785097e21c282807f5e845ec8f6d85 (diff)
Merge branch 'odin-lang:master' into core-image-tga
Diffstat (limited to 'src/check_decl.cpp')
-rw-r--r--src/check_decl.cpp220
1 files changed, 161 insertions, 59 deletions
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 7dd9db105..27babd255 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -162,8 +162,6 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e
if (found_scope == nullptr) {
return;
}
- rw_mutex_lock(&found_scope->mutex);
- defer (rw_mutex_unlock(&found_scope->mutex));
// IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
// original entity was still used check checked, but the checking was only
@@ -172,7 +170,9 @@ gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_e
// Therefore two things can be done: the type can be assigned to state that it
// has been "evaluated" and the variant data can be copied across
+ rw_mutex_lock(&found_scope->mutex);
string_map_set(&found_scope->elements, original_name, new_entity);
+ rw_mutex_unlock(&found_scope->mutex);
original_entity->flags |= EntityFlag_Overridden;
original_entity->type = new_entity->type;
@@ -559,6 +559,11 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
Type *super = ac.objc_superclass;
while (super != nullptr) {
+ if (super->kind != Type_Named) {
+ error(e->token, "@(objc_superclass) Referenced type must be a named struct");
+ break;
+ }
+
if (type_set_update(&super_set, super)) {
error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered");
break;
@@ -566,11 +571,6 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info);
- if (super->kind != Type_Named) {
- error(e->token, "@(objc_superclass) Referenced type must be a named struct");
- break;
- }
-
Type* named_type = base_named_type(super);
GB_ASSERT(named_type->kind == Type_Named);
@@ -587,9 +587,7 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
super = named_type->Named.type_name->TypeName.objc_superclass;
}
} else {
- if (ac.objc_superclass != nullptr) {
- error(e->token, "@(objc_superclass) may only be applied when the @(obj_implement) attribute is also applied");
- } else if (ac.objc_ivar != nullptr) {
+ if (ac.objc_ivar != nullptr) {
error(e->token, "@(objc_ivar) may only be applied when the @(obj_implement) attribute is also applied");
} else if (ac.objc_context_provider != nullptr) {
error(e->token, "@(objc_context_provider) may only be applied when the @(obj_implement) attribute is also applied");
@@ -851,6 +849,50 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) {
}
}
+ Type *x_base = base_type(x);
+ Type *y_base = base_type(y);
+
+ if (x_base == y_base) {
+ return true;
+ }
+
+ if (x_base->kind == y_base->kind &&
+ x_base->kind == Type_Struct) {
+ i64 xs = type_size_of(x_base);
+ i64 ys = type_size_of(y_base);
+
+ i64 xa = type_align_of(x_base);
+ i64 ya = type_align_of(y_base);
+
+
+ if (x_base->Struct.is_raw_union == y_base->Struct.is_raw_union &&
+ xs == ys && xa == ya) {
+ if (xs > 16) {
+ // @@ABI NOTE(bill): Just allow anything over 16-bytes to be allowed, because on all current ABIs
+ // it will be passed by point
+ // NOTE(bill): this must be changed when ABI changes
+ return true;
+ }
+ if (x_base->Struct.is_raw_union) {
+ return true;
+ }
+ if (x->Struct.fields.count == y->Struct.fields.count) {
+ for (isize i = 0; i < x->Struct.fields.count; i++) {
+ Entity *a = x->Struct.fields[i];
+ Entity *b = y->Struct.fields[i];
+ bool similar = signature_parameter_similar_enough(a->type, b->type);
+ if (!similar) {
+ // NOTE(bill): If the fields are not similar enough, then stop.
+ goto end;
+ }
+ }
+ }
+ // HACK NOTE(bill): Allow this for the time begin until it actually becomes a practical problem
+ return true;
+ }
+ }
+
+end:;
return are_types_identical(x, y);
}
@@ -948,7 +990,7 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e)
error(ident, "foreign library names must be an identifier");
} else {
String name = ident->Ident.token.string;
- Entity *found = scope_lookup(ctx->scope, name);
+ Entity *found = scope_lookup(ctx->scope, name, ident->Ident.hash);
if (found == nullptr) {
if (is_blank_ident(name)) {
@@ -1040,61 +1082,100 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
// Enable implementation by default if the class is an implementer too and
// @objc_implement was not set to false explicitly in this proc.
bool implement = tn->TypeName.objc_is_implementation;
+ if( ac.objc_is_implementation && !tn->TypeName.objc_is_implementation ) {
+ error(e->token, "Cannot apply @(objc_is_implement) to a procedure whose type does not also have @(objc_is_implement) set");
+ }
+
if (ac.objc_is_disabled_implement) {
implement = false;
}
- if (implement) {
- GB_ASSERT(e->kind == Entity_Procedure);
+ String objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
+
+ if (e->kind == Entity_Procedure) {
+ bool has_body = e->decl_info->proc_lit->ProcLit.body != nullptr;
+ e->Procedure.is_objc_impl_or_import = implement || !has_body;
+ e->Procedure.is_objc_class_method = ac.objc_is_class_method;
+ e->Procedure.objc_selector_name = objc_selector;
+ e->Procedure.objc_class = tn;
auto &proc = e->type->Proc;
Type *first_param = proc.param_count > 0 ? proc.params->Tuple.variables[0]->type : t_untyped_nil;
- if (!tn->TypeName.objc_is_implementation) {
- error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied");
- } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
- error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
- } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) {
- error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set");
- } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) {
- error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention");
- } else if (proc.result_count > 1) {
- error(e->token, "Objective-C method implementations may return at most 1 value");
- } else {
- // Always export unconditionally
- // NOTE(harold): This means check_objc_methods() MUST be called before
- // e->Procedure.is_export is set in check_proc_decl()!
- if (ac.is_export) {
- error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly");
- }
- if (ac.link_name != "") {
- error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly");
- }
+ if (implement) {
+ if( !has_body ) {
+ error(e->token, "Procedures with @(objc_is_implement) must have a body");
+ } else if (!tn->TypeName.objc_is_implementation) {
+ error(e->token, "@(objc_is_implement) attribute may only be applied to procedures whose class also have @(objc_is_implement) applied");
+ } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
+ error(e->token, "Objective-C instance methods implementations require the first parameter to be a pointer to the class type set by @(objc_type)");
+ } else if (proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) {
+ error(e->token, "Objective-C methods with Odin calling convention can only be used with classes that have @(objc_context_provider) set");
+ } else if (ac.objc_is_class_method && proc.calling_convention != ProcCC_CDecl) {
+ error(e->token, "Objective-C class methods (objc_is_class_method=true) that have @objc_is_implementation can only use \"c\" calling convention");
+ } else if (proc.result_count > 1) {
+ error(e->token, "Objective-C method implementations may return at most 1 value");
+ } else {
+ // Always export unconditionally
+ // NOTE(harold): This means check_objc_methods() MUST be called before
+ // e->Procedure.is_export is set in check_proc_decl()!
+ if (ac.is_export) {
+ error(e->token, "Explicit export not allowed when @(objc_implement) is set. It set exported implicitly");
+ }
+ if (ac.link_name != "") {
+ error(e->token, "Explicit linkage not allowed when @(objc_implement) is set. It set to \"strong\" implicitly");
+ }
- ac.is_export = true;
- ac.linkage = STR_LIT("strong");
+ ac.is_export = true;
+ ac.linkage = STR_LIT("strong");
- auto method = ObjcMethodData{ ac, e };
- method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
+ auto method = ObjcMethodData{ ac, e };
+ method.ac.objc_selector = objc_selector;
- CheckerInfo *info = ctx->info;
- mutex_lock(&info->objc_method_mutex);
- defer (mutex_unlock(&info->objc_method_mutex));
+ CheckerInfo *info = ctx->info;
+ mutex_lock(&info->objc_method_mutex);
+ defer (mutex_unlock(&info->objc_method_mutex));
- Array<ObjcMethodData>* method_list = map_get(&info->objc_method_implementations, t);
- if (method_list) {
- array_add(method_list, method);
- } else {
- auto list = array_make<ObjcMethodData>(permanent_allocator(), 1, 8);
- list[0] = method;
+ Array<ObjcMethodData>* method_list = map_get(&info->objc_method_implementations, t);
+ if (method_list) {
+ array_add(method_list, method);
+ } else {
+ auto list = array_make<ObjcMethodData>(permanent_allocator(), 1, 8);
+ list[0] = method;
- map_set(&info->objc_method_implementations, t, list);
+ map_set(&info->objc_method_implementations, t, list);
+ }
+ }
+ } else if (!has_body) {
+ if (ac.objc_selector == "The @(objc_selector) attribute is required for imported Objective-C methods.") {
+ return;
+ } else if (proc.calling_convention != ProcCC_CDecl) {
+ error(e->token, "Imported Objective-C methods must use the \"c\" calling convention");
+ return;
+ } else if (tn->TypeName.objc_context_provider) {
+ error(e->token, "Imported Objective-C class '%.*s' must not declare context providers.", tn->type->Named.name);
+ return;
+ } else if (tn->TypeName.objc_is_implementation) {
+ error(e->token, "Imported Objective-C methods used in a class with @(objc_implement) is not allowed.");
+ return;
+ } else if (!ac.objc_is_class_method && !(first_param->kind == Type_Pointer && internal_check_is_assignable_to(t, first_param->Pointer.elem))) {
+ error(e->token, "Objective-C instance methods require the first parameter to be a pointer to the class type set by @(objc_type)");
+ return;
}
}
- } else if (ac.objc_selector != "") {
- error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations.");
+ else if(ac.objc_selector != "") {
+ error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C method implementations or are imported.");
+ return;
+ }
+ } else {
+ GB_ASSERT(e->kind == Entity_ProcGroup);
+ if (tn->TypeName.objc_is_implementation) {
+ error(e->token, "Objective-C procedure groups cannot use the @(objc_implement) attribute.");
+ return;
+ }
}
+
mutex_lock(&global_type_name_objc_metadata_mutex);
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
@@ -1479,7 +1560,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
if (!pt->is_polymorphic) {
check_procedure_later(ctx->checker, ctx->file, e->token, d, proc_type, pl->body, pl->tags);
}
- } else if (!is_foreign) {
+ } else if (!is_foreign && !e->Procedure.is_objc_impl_or_import) {
if (e->Procedure.is_export) {
error(e->token, "Foreign export procedures must have a body");
} else {
@@ -1526,7 +1607,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
if (is_arch_wasm() && foreign_library != nullptr) {
// NOTE(bill): this must be delayed because the foreign import paths might not be evaluated yet until much later
mpsc_enqueue(&ctx->info->foreign_decls_to_check, e);
- } else {
+ } else if (!e->Procedure.is_objc_impl_or_import) {
check_foreign_procedure(ctx, e, d);
}
} else {
@@ -1549,7 +1630,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
"\tother at %s",
LIT(name), token_pos_to_string(pos));
} else if (name == "main") {
- if (d->entity->pkg->kind != Package_Runtime) {
+ if (d->entity.load()->pkg->kind != Package_Runtime) {
error(d->proc_lit, "The link name 'main' is reserved for internal use");
}
} else {
@@ -1565,7 +1646,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
-gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr, Ast *init_expr) {
+gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) {
GB_ASSERT(e->type == nullptr);
GB_ASSERT(e->kind == Entity_Variable);
@@ -1686,7 +1767,28 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
check_expr_with_type_hint(ctx, &o, init_expr, e->type);
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
if (e->Variable.is_rodata && o.mode != Addressing_Constant) {
+ ERROR_BLOCK();
error(o.expr, "Variables declared with @(rodata) must have constant initialization");
+ Ast *expr = unparen_expr(o.expr);
+ if (is_type_struct(e->type) && expr && expr->kind == Ast_CompoundLit) {
+ ast_node(cl, CompoundLit, expr);
+ for (Ast *elem_ : cl->elems) {
+ Ast *elem = elem_;
+ if (elem->kind == Ast_FieldValue) {
+ elem = elem->FieldValue.value;
+ }
+ elem = unparen_expr(elem);
+
+ Entity *e = entity_of_node(elem);
+ if (elem->tav.mode != Addressing_Constant && e == nullptr && elem->kind != Ast_ProcLit) {
+ Token tok = ast_token(elem);
+ TokenPos pos = tok.pos;
+ gbString s = type_to_string(type_of_expr(elem));
+ error_line("%s Element is not constant, which is required for @(rodata), of type %s\n", token_pos_to_string(pos), s);
+ gb_string_free(s);
+ }
+ }
+ }
}
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
@@ -1915,7 +2017,7 @@ gb_internal void add_deps_from_child_to_parent(DeclInfo *decl) {
rw_mutex_shared_lock(&decl->deps_mutex);
rw_mutex_lock(&decl->parent->deps_mutex);
- for (Entity *e : decl->deps) {
+ FOR_PTR_SET(e, decl->deps) {
ptr_set_add(&decl->parent->deps, e);
}
@@ -1967,8 +2069,8 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
ctx->curr_proc_sig = type;
ctx->curr_proc_calling_convention = type->Proc.calling_convention;
- if (decl->parent && decl->entity && decl->parent->entity) {
- decl->entity->parent_proc_decl = decl->parent;
+ if (decl->parent && decl->entity.load() && decl->parent->entity) {
+ decl->entity.load()->parent_proc_decl = decl->parent;
}
if (ctx->pkg->name != "runtime") {
@@ -1981,9 +2083,9 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
ast_node(bs, BlockStmt, body);
+ TEMPORARY_ALLOCATOR_GUARD();
Array<ProcUsingVar> using_entities = {};
- using_entities.allocator = heap_allocator();
- defer (array_free(&using_entities));
+ using_entities.allocator = temporary_allocator();
{
if (type->Proc.param_count > 0) {
@@ -2072,7 +2174,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
GB_ASSERT(decl->proc_checked_state != ProcCheckedState_Checked);
if (decl->defer_use_checked) {
GB_ASSERT(is_type_polymorphic(type, true));
- error(token, "Defer Use Checked: %.*s", LIT(decl->entity->token.string));
+ error(token, "Defer Use Checked: %.*s", LIT(decl->entity.load()->token.string));
GB_ASSERT(decl->defer_use_checked == false);
}