aboutsummaryrefslogtreecommitdiff
path: root/src/checker.cpp
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2025-05-22 16:04:42 +0100
committerGitHub <noreply@github.com>2025-05-22 16:04:42 +0100
commitc383e550f9da373631bea702c4614f62eb7ad804 (patch)
treeadd4106cbde3d52df97cef761622a0719ded61a6 /src/checker.cpp
parentea65a7b870736311747c517970df3921d227e024 (diff)
parent93d2e6aca2c771477b133af0decfefa3eb607ab9 (diff)
Merge branch 'master' into bill/raddebugger-custom-section
Diffstat (limited to 'src/checker.cpp')
-rw-r--r--src/checker.cpp137
1 files changed, 135 insertions, 2 deletions
diff --git a/src/checker.cpp b/src/checker.cpp
index 5e1517875..aaa815365 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -728,12 +728,17 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl
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) {
+ } 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->flags & EntityFlag_Init) {
+ is_unused = false;
+ } else if (e->flags & EntityFlag_Fini) {
+ is_unused = false;
+ } else if (e->Procedure.is_export) {
+ is_unused = false;
} else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") {
is_unused = false;
} else {
@@ -1351,10 +1356,12 @@ gb_internal void init_universal(void) {
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_ivar = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_ivar"), alloc_type_struct_complete());
t_objc_id = alloc_type_pointer(t_objc_object);
t_objc_SEL = alloc_type_pointer(t_objc_selector);
t_objc_Class = alloc_type_pointer(t_objc_class);
+ t_objc_Ivar = alloc_type_pointer(t_objc_ivar);
}
}
@@ -1387,6 +1394,10 @@ gb_internal void init_checker_info(CheckerInfo *i) {
array_init(&i->defineables, a);
map_init(&i->objc_msgSend_types);
+ mpsc_init(&i->objc_class_implementations, a);
+ string_set_init(&i->obcj_class_name_set, 0);
+ map_init(&i->objc_method_implementations);
+
string_map_init(&i->load_file_cache);
array_init(&i->all_procedures, heap_allocator());
@@ -1497,6 +1508,8 @@ gb_internal void init_checker(Checker *c) {
TIME_SECTION("init proc queues");
mpsc_init(&c->procs_with_deferred_to_check, a); //, 1<<10);
+ mpsc_init(&c->procs_with_objc_context_provider_to_check, a);
+
// NOTE(bill): 1 Mi elements should be enough on average
array_init(&c->procs_to_check, heap_allocator(), 0, 1<<20);
@@ -3662,6 +3675,33 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
}
return true;
+ } else if (name == "objc_implement") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Bool) {
+ ac->objc_is_implementation = ev.value_bool;
+
+ if (!ac->objc_is_implementation) {
+ ac->objc_is_disabled_implement = true;
+ }
+ } else if (ev.kind == ExactValue_Invalid) {
+ ac->objc_is_implementation = true;
+ } else {
+ error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
+ }
+
+ return true;
+ } else if (name == "objc_selector") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ if (string_is_valid_identifier(ev.value_string)) {
+ ac->objc_selector = ev.value_string;
+ } else {
+ error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
} else if (name == "require_target_feature") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
@@ -3907,6 +3947,51 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
ac->objc_class = ev.value_string;
}
return true;
+ } else if (name == "objc_implement") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Bool) {
+ ac->objc_is_implementation = ev.value_bool;
+ } else if (ev.kind == ExactValue_Invalid) {
+ ac->objc_is_implementation = true;
+ } else {
+ error(elem, "Expected a boolean value, or no value, for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_superclass") {
+ Type *objc_superclass = check_type(c, value);
+
+ if (objc_superclass != nullptr) {
+ ac->objc_superclass = objc_superclass;
+ } else {
+ error(value, "'%.*s' expected a named type", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_ivar") {
+ Type *objc_ivar = check_type(c, value);
+
+ if (objc_ivar != nullptr && objc_ivar->kind == Type_Named) {
+ ac->objc_ivar = objc_ivar;
+ } else {
+ error(value, "'%.*s' expected a named type", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_context_provider") {
+ Operand o = {};
+ check_expr(c, &o, value);
+ Entity *e = entity_of_node(o.expr);
+
+ if (e != nullptr) {
+ if (ac->objc_context_provider != nullptr) {
+ error(elem, "Previous usage of a 'objc_context_provider' attribute");
+ }
+ if (e->kind != Entity_Procedure) {
+ error(elem, "'objc_context_provider' must refer to a procedure");
+ } else {
+ ac->objc_context_provider = e;
+ }
+
+ return true;
+ }
}
return false;
}
@@ -6240,6 +6325,12 @@ gb_internal void check_deferred_procedures(Checker *c) {
continue;
}
+ if (dst->flags & EntityFlag_Disabled) {
+ // Prevent procedures that have been disabled from acting as deferrals.
+ src->Procedure.deferred_procedure = {};
+ continue;
+ }
+
GB_ASSERT(is_type_proc(src->type));
GB_ASSERT(is_type_proc(dst->type));
Type *src_params = base_type(src->type)->Proc.params;
@@ -6395,6 +6486,44 @@ gb_internal void check_deferred_procedures(Checker *c) {
}
+gb_internal void check_objc_context_provider_procedures(Checker *c) {
+ for (Entity *e = nullptr; mpsc_dequeue(&c->procs_with_objc_context_provider_to_check, &e); /**/) {
+ GB_ASSERT(e->kind == Entity_TypeName);
+
+ Entity *proc_entity = e->TypeName.objc_context_provider;
+ GB_ASSERT(proc_entity->kind == Entity_Procedure);
+
+ auto &proc = proc_entity->type->Proc;
+
+ Type *return_type = proc.result_count != 1 ? t_untyped_nil : base_named_type(proc.results->Tuple.variables[0]->type);
+ if (return_type != t_context) {
+ error(proc_entity->token, "The @(objc_context_provider) procedure must only return a context.");
+ }
+
+ const char *self_param_err = "The @(objc_context_provider) procedure must take as a parameter a single pointer to the @(objc_type) value.";
+ if (proc.param_count != 1) {
+ error(proc_entity->token, self_param_err);
+ }
+
+ Type *self_param = base_type(proc.params->Tuple.variables[0]->type);
+ if (self_param->kind != Type_Pointer) {
+ error(proc_entity->token, self_param_err);
+ }
+
+ Type *self_type = base_named_type(self_param->Pointer.elem);
+ if (!internal_check_is_assignable_to(self_type, e->type) &&
+ !(e->TypeName.objc_ivar && internal_check_is_assignable_to(self_type, e->TypeName.objc_ivar))) {
+ error(proc_entity->token, self_param_err);
+ }
+ if (proc.calling_convention != ProcCC_CDecl && proc.calling_convention != ProcCC_Contextless) {
+ error(e->token, self_param_err);
+ }
+ if (proc.is_polymorphic) {
+ error(e->token, self_param_err);
+ }
+ }
+}
+
gb_internal void check_unique_package_names(Checker *c) {
ERROR_BLOCK();
@@ -6555,6 +6684,7 @@ gb_internal void check_update_dependency_tree_for_procedures(Checker *c) {
}
}
+
gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("map full filepaths to scope");
add_type_info_type(&c->builtin_ctx, t_invalid);
@@ -6664,6 +6794,9 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check deferred procedures");
check_deferred_procedures(c);
+ TIME_SECTION("check objc context provider procedures");
+ check_objc_context_provider_procedures(c);
+
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);