mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-07 02:54:18 +00:00
Merge pull request #5064 from harold-b/hb/objc-classes
Add support for Objective-C class implementation
This commit is contained in:
122
src/checker.cpp
122
src/checker.cpp
@@ -1351,10 +1351,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 +1389,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 +1503,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 +3670,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 +3942,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;
|
||||
}
|
||||
@@ -6395,6 +6475,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 +6673,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 +6783,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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user