aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/check_builtin.cpp59
-rw-r--r--src/check_decl.cpp122
-rw-r--r--src/check_expr.cpp73
-rw-r--r--src/checker.cpp8
-rw-r--r--src/checker_builtin_procs.hpp4
-rw-r--r--src/entity.cpp4
-rw-r--r--src/llvm_backend.cpp170
-rw-r--r--src/llvm_backend_proc.cpp22
-rw-r--r--src/llvm_backend_utility.cpp212
-rw-r--r--src/parser.hpp3
-rw-r--r--src/types.cpp11
11 files changed, 551 insertions, 137 deletions
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 7e1567750..f142f04b7 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -210,7 +210,7 @@ gb_internal ObjcMsgKind get_objc_proc_kind(Type *return_type) {
return ObjcMsg_normal;
}
-gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
+void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types) {
ObjcMsgKind kind = get_objc_proc_kind(return_type);
Scope *scope = create_scope(c->info, nullptr);
@@ -248,6 +248,12 @@ gb_internal void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_t
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
+
+ Slice<Ast *> args = call->CallExpr.args;
+ if (args.count > 0 && args[0]->tav.objc_super_target) {
+ try_to_add_package_dependency(c, "runtime", "objc_msgSendSuper2");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSendSuper2_stret");
+ }
}
gb_internal bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
@@ -466,8 +472,8 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
isize capture_arg_count = ce->args.count - 1;
- // NOTE(harold): The first parameter is already checked at check_builtin_procedure().
- // Checking again would invalidate the Entity -> Value map for direct parameters if it's the handler proc.
+ // NOTE(harold): The first argument is already checked at check_builtin_procedure().
+ // Checking again would invalidate the Entity -> Value map for direct arguments if it's the handler proc.
param_operands[0] = *operand;
for (isize i = 0; i < ce->args.count-1; i++) {
@@ -680,6 +686,52 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
operand->mode = Addressing_Value;
return true;
} break;
+
+ case BuiltinProc_objc_super:
+ {
+ // Must be a pointer to an Objective-C object.
+ Type *objc_obj = operand->type;
+ if (!is_type_objc_ptr_to_object(objc_obj)) {
+ gbString e = expr_to_string(operand->expr);
+ gbString t = type_to_string(objc_obj);
+ error(operand->expr, "'%.*s' expected a pointer to an Objective-C object, but got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ if (operand->mode != Addressing_Value && operand->mode != Addressing_Variable) {
+ gbString e = expr_to_string(operand->expr);
+ gbString t = type_to_string(operand->type);
+ error(operand->expr, "'%.*s' expression '%s', of type %s, must be a value or variable.", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ Type *obj_type = type_deref(objc_obj);
+ GB_ASSERT(obj_type->kind == Type_Named);
+
+ // NOTE(harold) Track original type before transforming it to the superclass.
+ // This is needed because objc_msgSendSuper2 must start its search on the subclass, not the superclass.
+ call->tav.objc_super_target = obj_type;
+
+ // The superclass type must be known at compile time. We require this so that the selector method expressions
+ // methods are resolved to the superclass's methods instead of the subclass's.
+ Type *superclass = obj_type->Named.type_name->TypeName.objc_superclass;
+ if (superclass == nullptr) {
+ gbString t = type_to_string(obj_type);
+ error(operand->expr, "'%.*s' target object '%.*s' does not have an Objective-C superclass. One must be set via the @(objc_superclass) attribute", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ GB_ASSERT(superclass->Named.type_name->TypeName.objc_class_name.len > 0);
+
+ operand->type = alloc_type_pointer(superclass);
+ return true;
+
+ } break;
}
}
@@ -2515,6 +2567,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_objc_register_class:
case BuiltinProc_objc_ivar_get:
case BuiltinProc_objc_block:
+ case BuiltinProc_objc_super:
return check_builtin_objc_procedure(c, operand, call, id, type_hint);
case BuiltinProc___entry_point:
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 842f8653c..113c1e171 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -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");
@@ -1040,61 +1038,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 +1516,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 {
@@ -1527,6 +1564,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
// 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 {
+ // TODO(harold): Check if it's an objective-C foreign, if so, I don't think we need to check it.
check_foreign_procedure(ctx, e, d);
}
} else {
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 2a22e5c48..5f36bf3a1 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -8151,6 +8151,73 @@ gb_internal ExprKind check_call_expr_as_type_cast(CheckerContext *c, Operand *op
}
+void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice<Type *> param_types);
+
+gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *call, Entity *proc_entity, Type *proc_type) {
+ auto &proc = proc_type->Proc;
+ Slice<Entity *> params = proc.params ? proc.params->Tuple.variables : Slice<Entity *>{};
+
+ Type *self_type = nullptr;
+ isize params_start = 1;
+
+ ast_node(ce, CallExpr, call);
+
+ Type *return_type = proc.result_count == 0 ? nullptr : proc.results->Tuple.variables[0]->type;
+ bool is_return_instancetype = return_type != nullptr && return_type == t_objc_instancetype;
+
+ if (params.count == 0 || !is_type_objc_ptr_to_object(params[0]->type)) {
+ if (!proc_entity->Procedure.is_objc_class_method) {
+ // Not a class method, invalid call
+ error(call, "Invalid Objective-C call: The Objective-C method is not a class method but this first parameter is not an Objective-C object pointer.");
+ return;
+ }
+
+ if (is_return_instancetype) {
+ if (ce->proc->kind == Ast_SelectorExpr) {
+ ast_node(se, SelectorExpr, ce->proc);
+
+ // NOTE(harold): These should have already been checked, right?
+ GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named);
+
+ return_type = alloc_type_pointer(se->expr->tav.type);
+ } else {
+ return_type = proc_entity->Procedure.objc_class->type;
+ }
+ }
+
+ self_type = t_objc_Class;
+ params_start = 0;
+ } else if (ce->args.count > 0) {
+ GB_ASSERT(is_type_objc_ptr_to_object(params[0]->type));
+
+ if (ce->args[0]->tav.objc_super_target) {
+ self_type = t_objc_super_ptr;
+ } else {
+ self_type = ce->args[0]->tav.type;
+ }
+
+ if (is_return_instancetype) {
+ // NOTE(harold): These should have already been checked, right?
+ GB_ASSERT(ce->args[0]->tav.type && ce->args[0]->tav.type->kind == Type_Pointer && ce->args[0]->tav.type->Pointer.elem->kind == Type_Named);
+
+ return_type = ce->args[0]->tav.type;
+ }
+ }
+
+ auto param_types = slice_make<Type *>(permanent_allocator(), proc.param_count + 2 - params_start);
+ param_types[0] = self_type;
+ param_types[1] = t_objc_SEL;
+
+ for (isize i = params_start; i < params.count; i++) {
+ param_types[i+2-params_start] = params[i]->type;
+ }
+
+ if (is_return_instancetype) {
+ operand->type = return_type;
+ }
+
+ add_objc_proc_type(c, call, return_type, param_types);
+}
gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
if (proc != nullptr &&
@@ -8414,6 +8481,12 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
+ Entity *proc_entity = entity_from_expr(call->CallExpr.proc);
+ bool is_objc_call = proc_entity && proc_entity->kind == Entity_Procedure && proc_entity->Procedure.is_objc_impl_or_import;
+ if (is_objc_call) {
+ check_objc_call_expr(c, operand, call, proc_entity, pt);
+ }
+
return Expr_Expr;
}
diff --git a/src/checker.cpp b/src/checker.cpp
index 32bda2e43..d3c111de4 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -1416,6 +1416,8 @@ gb_internal void init_universal(void) {
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);
+
+ t_objc_instancetype = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_instancetype"), t_objc_id);
}
}
@@ -3386,12 +3388,18 @@ gb_internal void init_core_map_type(Checker *c) {
t_raw_map_ptr = alloc_type_pointer(t_raw_map);
}
+gb_internal void init_core_objc_c(Checker *c) {
+ t_objc_super = find_core_type(c, str_lit("objc_super"));
+ t_objc_super_ptr = alloc_type_pointer(t_objc_super);
+}
+
gb_internal void init_preload(Checker *c) {
init_core_type_info(c);
init_mem_allocator(c);
init_core_context(c);
init_core_source_code_location(c);
init_core_map_type(c);
+ init_core_objc_c(c);
}
gb_internal ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index c6071bf98..c2255a6ba 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -354,6 +354,7 @@ BuiltinProc__type_end,
BuiltinProc_objc_register_class,
BuiltinProc_objc_ivar_get,
BuiltinProc_objc_block,
+ BuiltinProc_objc_super,
BuiltinProc_constant_utf16_cstring,
@@ -715,7 +716,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("objc_ivar_get"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
- {STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("objc_block"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("objc_super"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
diff --git a/src/entity.cpp b/src/entity.cpp
index d6d8f58de..2b21fdcac 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -251,6 +251,8 @@ struct Entity {
String link_name;
String link_prefix;
String link_suffix;
+ String objc_selector_name;
+ Entity *objc_class;
DeferredProcedure deferred_procedure;
struct GenProcsData *gen_procs;
@@ -266,6 +268,8 @@ struct Entity {
bool is_anonymous : 1;
bool no_sanitize_address : 1;
bool no_sanitize_memory : 1;
+ bool is_objc_impl_or_import : 1;
+ bool is_objc_class_method : 1;
} Procedure;
struct {
Array<Entity *> entities;
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index b47e2788f..86c83b91f 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -1417,8 +1417,21 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
return str_lit("?");
case Type_Proc:
return str_lit("?");
- case Type_BitSet:
- return lb_get_objc_type_encoding(t->BitSet.underlying, pointer_depth);
+ case Type_BitSet: {
+ Type *bitset_integer_type = t->BitSet.underlying;
+ if (!bitset_integer_type) {
+ switch (t->cached_size) {
+ case 1: bitset_integer_type = t_u8; break;
+ case 2: bitset_integer_type = t_u16; break;
+ case 4: bitset_integer_type = t_u32; break;
+ case 8: bitset_integer_type = t_u64; break;
+ case 16: bitset_integer_type = t_u128; break;
+ }
+ }
+ GB_ASSERT_MSG(bitset_integer_type, "Could not determine bit_set integer size for objc_type_encoding");
+
+ return lb_get_objc_type_encoding(bitset_integer_type, pointer_depth);
+ }
case Type_SimdVector: {
String type_str = lb_get_objc_type_encoding(t->SimdVector.elem, pointer_depth);
@@ -1452,7 +1465,10 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) {
struct lbObjCGlobalClass {
lbObjCGlobal g;
- lbValue class_value; // Local registered class value
+ union {
+ lbValue class_value; // Local registered class value
+ lbAddr class_global; // Global class pointer. Placeholder for class implementations which are registered in order of definition.
+ };
};
gb_internal void lb_register_objc_thing(
@@ -1482,44 +1498,43 @@ gb_internal void lb_register_objc_thing(
LLVMSetInitializer(v.value, LLVMConstNull(t));
}
- lbValue class_ptr = {};
- lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name));
-
// If this class requires an implementation, save it for registration below.
if (g.class_impl_type != nullptr) {
// Make sure the superclass has been initialized before us
- lbValue superclass_value = lb_const_nil(m, t_objc_Class);
-
auto &tn = g.class_impl_type->Named.type_name->TypeName;
Type *superclass = tn.objc_superclass;
if (superclass != nullptr) {
auto& superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name);
lb_register_objc_thing(handled, m, args, class_impls, class_map, p, superclass_global.g, call);
- GB_ASSERT(superclass_global.class_value.value);
-
- superclass_value = superclass_global.class_value;
+ GB_ASSERT(superclass_global.class_global.addr.value);
}
- args.count = 3;
- args[0] = superclass_value;
- args[1] = class_name;
- args[2] = lb_const_int(m, t_uint, 0);
- class_ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
+ lbObjCGlobalClass impl_global = {};
+ impl_global.g = g;
+ impl_global.class_global = addr;
- array_add(&class_impls, lbObjCGlobalClass{g, class_ptr});
+ array_add(&class_impls, impl_global);
+
+ lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
+ if (class_global != nullptr) {
+ class_global->class_global = addr;
+ }
}
else {
+ lbValue class_ptr = {};
+ lbValue class_name = lb_const_value(m, t_cstring, exact_value_string(g.name));
+
args.count = 1;
args[0] = class_name;
class_ptr = lb_emit_runtime_call(p, call, args);
- }
- lb_addr_store(p, addr, class_ptr);
+ lb_addr_store(p, addr, class_ptr);
- lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
- if (class_global != nullptr) {
- class_global->class_value = class_ptr;
+ lbObjCGlobalClass* class_global = string_map_get(&class_map, g.name);
+ if (class_global != nullptr) {
+ class_global->class_value = class_ptr;
+ }
}
}
@@ -1582,7 +1597,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
string_map_init(&global_class_map, (usize)gen->objc_classes.count);
defer (string_map_destroy(&global_class_map));
- for (lbObjCGlobal g :referenced_classes) {
+ for (lbObjCGlobal g : referenced_classes) {
string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g});
}
@@ -1629,9 +1644,36 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
for (const auto &cd : class_impls) {
auto &g = cd.g;
- Type *class_type = g.class_impl_type;
+
+ Type *class_type = g.class_impl_type;
Type *class_ptr_type = alloc_type_pointer(class_type);
- lbValue class_value = cd.class_value;
+
+ // Begin class registration: create class pair and update global reference
+ lbValue class_value = {};
+
+ {
+ lbValue superclass_value = lb_const_nil(m, t_objc_Class);
+
+ auto& tn = class_type->Named.type_name->TypeName;
+ Type *superclass = tn.objc_superclass;
+
+ if (superclass != nullptr) {
+ auto& superclass_global = string_map_must_get(&global_class_map, superclass->Named.type_name->TypeName.objc_class_name);
+ superclass_value = superclass_global.class_value;
+ }
+
+ args.count = 3;
+ args[0] = superclass_value;
+ args[1] = lb_const_value(m, t_cstring, exact_value_string(g.name));
+ args[2] = lb_const_int(m, t_uint, 0);
+ class_value = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
+
+ lbObjCGlobalClass &mapped_global = string_map_must_get(&global_class_map, tn.objc_class_name);
+ lb_addr_store(p, mapped_global.class_global, class_value);
+
+ mapped_global.class_value = class_value;
+ }
+
Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar;
@@ -1651,7 +1693,6 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
is_context_provider_ivar = ivar_type != nullptr && internal_check_is_assignable_to(contex_provider_self_named_type, ivar_type);
}
-
Array<ObjcMethodData> *methods = map_get(&m->info->objc_method_implementations, class_type);
if (!methods) {
continue;
@@ -1710,17 +1751,21 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
wrapper_results_tuple, method_type->Proc.result_count, false, ProcCC_CDecl);
lbProcedure *wrapper_proc = lb_create_dummy_procedure(m, proc_name, wrapper_proc_type);
- lb_add_attribute_to_proc(wrapper_proc->module, wrapper_proc->value, "nounwind");
+
+ lb_add_function_type_attributes(wrapper_proc->value, lb_get_function_type(m, wrapper_proc_type), ProcCC_CDecl);
// Emit the wrapper
- LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage);
+ // LLVMSetLinkage(wrapper_proc->value, LLVMInternalLinkage);
+ LLVMSetDLLStorageClass(wrapper_proc->value, LLVMDLLExportStorageClass);
+ lb_add_attribute_to_proc(wrapper_proc->module, wrapper_proc->value, "nounwind");
+
lb_begin_procedure_body(wrapper_proc);
{
+ LLVMValueRef context_addr = nullptr;
if (method_type->Proc.calling_convention == ProcCC_Odin) {
GB_ASSERT(context_provider);
// Emit the get odin context call
-
get_context_args[0] = lbValue {
wrapper_proc->raw_input_parameters[0],
contex_provider_self_ptr_type,
@@ -1736,44 +1781,58 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
get_context_args[0] = lb_handle_objc_ivar_for_objc_object_pointer(wrapper_proc, real_self);
}
- lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args);
- lbAddr context_addr = lb_addr(lb_address_from_load_or_generate_local(wrapper_proc, context));
- lb_push_context_onto_stack(wrapper_proc, context_addr);
+ lbValue context = lb_emit_call(wrapper_proc, context_provider_proc_value, get_context_args);
+ context_addr = lb_address_from_load(wrapper_proc, context).value;//lb_address_from_load_or_generate_local(wrapper_proc, context));
+ // context_addr = LLVMGetOperand(context.value, 0);
}
+ isize method_forward_arg_count = method_param_count + method_param_offset;
+ isize method_forward_return_arg_offset = 0;
+ auto raw_method_args = array_make<LLVMValueRef>(temporary_allocator(), 0, method_forward_arg_count+1);
- auto method_call_args = array_make<lbValue>(temporary_allocator(), method_param_count + method_param_offset);
+ lbValue method_proc_value = lb_find_procedure_value_from_entity(m, md.proc_entity);
+ lbFunctionType* ft = lb_get_function_type(m, method_type);
+ bool has_return = false;
+ lbArgKind return_kind = {};
+
+ if (wrapper_results_tuple != nullptr) {
+ has_return = true;
+ return_kind = ft->ret.kind;
+
+ if (return_kind == lbArg_Indirect) {
+ method_forward_return_arg_offset = 1;
+ array_add(&raw_method_args, wrapper_proc->return_ptr.addr.value);
+ }
+ }
if (!md.ac.objc_is_class_method) {
- method_call_args[0] = lbValue {
- wrapper_proc->raw_input_parameters[0],
- class_ptr_type,
- };
+ array_add(&raw_method_args, wrapper_proc->raw_input_parameters[method_forward_return_arg_offset]);
}
for (isize i = 0; i < method_param_count; i++) {
- method_call_args[i+method_param_offset] = lbValue {
- wrapper_proc->raw_input_parameters[i+2],
- method_type->Proc.params->Tuple.variables[i+method_param_offset]->type,
- };
+ array_add(&raw_method_args, wrapper_proc->raw_input_parameters[i+2+method_forward_return_arg_offset]);
+ }
+
+ if (method_type->Proc.calling_convention == ProcCC_Odin) {
+ array_add(&raw_method_args, context_addr);
}
- lbValue method_proc_value = lb_find_procedure_value_from_entity(m, md.proc_entity);
// Call real procedure for method from here, passing the parameters expected, if any.
- lbValue return_value = lb_emit_call(wrapper_proc, method_proc_value, method_call_args);
+ LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(m, method_type);
+ LLVMValueRef ret_val_raw = LLVMBuildCall2(wrapper_proc->builder, fnp, method_proc_value.value, raw_method_args.data, (unsigned)raw_method_args.count, "");
- if (wrapper_results_tuple != nullptr) {
- auto &result_var = method_type->Proc.results->Tuple.variables[0];
- return_value = lb_emit_conv(wrapper_proc, return_value, result_var->type);
- lb_build_return_stmt_internal(wrapper_proc, return_value, result_var->token.pos);
+ if (has_return && return_kind != lbArg_Indirect) {
+ LLVMBuildRet(wrapper_proc->builder, ret_val_raw);
+ }
+ else {
+ LLVMBuildRetVoid(wrapper_proc->builder);
}
}
lb_end_procedure_body(wrapper_proc);
-
// Add the method to the class
String method_encoding = str_lit("v");
- // TODO (harold): Checker must ensure that objc_methods have a single return value or none!
+
GB_ASSERT(method_type->Proc.result_count <= 1);
if (method_type->Proc.result_count != 0) {
method_encoding = lb_get_objc_type_encoding(method_type->Proc.results->Tuple.variables[0]->type);
@@ -1785,8 +1844,8 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:"));
}
- for (isize i = method_param_offset; i < method_param_count; i++) {
- Type *param_type = method_type->Proc.params->Tuple.variables[i]->type;
+ for (isize i = 0; i < method_param_count; i++) {
+ Type *param_type = method_type->Proc.params->Tuple.variables[i + method_param_offset]->type;
String param_encoding = lb_get_objc_type_encoding(param_type);
method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding);
@@ -1805,7 +1864,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
args[2] = lbValue { wrapper_proc->value, wrapper_proc->type };
args[3] = lb_const_value(m, t_cstring, exact_value_string(method_encoding));
- // TODO(harold): Emit check BOOL result and panic if false.
+ // TODO(harold): Emit check BOOL result and panic if false?
lb_emit_runtime_call(p, "class_addMethod", args);
} // End methods
@@ -1853,7 +1912,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
// Defined in an external package, define it now in the main package
LLVMTypeRef t = lb_type(m, t_int);
- lbValue global{};
+ lbValue global = {};
global.value = LLVMAddGlobal(m->mod, t, g.global_name);
global.type = t_int_ptr;
@@ -2192,6 +2251,11 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
GB_ASSERT(m != nullptr);
if (e->kind == Entity_Procedure) {
+ if (e->Procedure.is_foreign && e->Procedure.is_objc_impl_or_import) {
+ // Do not generate declarations for foreign Objective-C methods. These are called indirectly through the Objective-C runtime.
+ continue;
+ }
+
array_add(&m->global_procedures_to_create, e);
} else if (e->kind == Entity_TypeName) {
array_add(&m->global_types_to_create, e);
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 7680c5e76..3bd5f4ef2 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -3753,6 +3753,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
case BuiltinProc_objc_ivar_get: return lb_handle_objc_ivar_get(p, expr);
case BuiltinProc_objc_block: return lb_handle_objc_block(p, expr);
+ case BuiltinProc_objc_super: return lb_handle_objc_super(p, expr);
case BuiltinProc_constant_utf16_cstring:
@@ -4122,21 +4123,23 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
Ast *proc_expr = unparen_expr(ce->proc);
+ Entity *proc_entity = entity_of_node(proc_expr);
+
if (proc_mode == Addressing_Builtin) {
- Entity *e = entity_of_node(proc_expr);
BuiltinProcId id = BuiltinProc_Invalid;
- if (e != nullptr) {
- id = cast(BuiltinProcId)e->Builtin.id;
+ if (proc_entity != nullptr) {
+ id = cast(BuiltinProcId)proc_entity->Builtin.id;
} else {
id = BuiltinProc_DIRECTIVE;
}
return lb_build_builtin_proc(p, expr, tv, id);
}
+ bool is_objc_call = proc_entity->Procedure.is_objc_impl_or_import;
+
// NOTE(bill): Regular call
lbValue value = {};
- Entity *proc_entity = entity_of_node(proc_expr);
if (proc_entity != nullptr) {
if (proc_entity->flags & EntityFlag_Disabled) {
GB_ASSERT(tv.type == nullptr);
@@ -4170,11 +4173,13 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
}
- if (value.value == nullptr) {
+ if (is_objc_call) {
+ value.type = proc_tv.type;
+ } else if (value.value == nullptr) {
value = lb_build_expr(p, proc_expr);
}
- GB_ASSERT(value.value != nullptr);
+ GB_ASSERT(value.value != nullptr || is_objc_call);
Type *proc_type_ = base_type(value.type);
GB_ASSERT(proc_type_->kind == Type_Proc);
TypeProc *pt = &proc_type_->Proc;
@@ -4402,6 +4407,11 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
isize final_count = is_c_vararg ? args.count : pt->param_count;
auto call_args = array_slice(args, 0, final_count);
+
+ if (is_objc_call) {
+ return lb_handle_objc_auto_send(p, expr, slice(call_args, 0, call_args.count));
+ }
+
return lb_emit_call(p, value, call_args, ce->inlining);
}
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 7fe6b1458..d124f164e 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -2373,7 +2373,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
/// https://www.newosxbook.com/src.php?tree=xnu&file=/libkern/libkern/Block_private.h
/// https://github.com/llvm/llvm-project/blob/21f1f9558df3830ffa637def364e3c0cb0dbb3c0/compiler-rt/lib/BlocksRuntime/Block_private.h
/// https://github.com/apple-oss-distributions/libclosure/blob/3668b0837f47be3cc1c404fb5e360f4ff178ca13/runtime.cpp
-
+ // TODO(harold): Ensure we don't have any issues with large struct arguments or returns in block wrappers.
ast_node(ce, CallExpr, expr);
GB_ASSERT(ce->args.count > 0);
@@ -2452,7 +2452,9 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
lbProcedure *invoker_proc = lb_create_dummy_procedure(m, make_string((u8*)block_invoker_name,
gb_string_length(block_invoker_name)), invoker_proc_type);
+
LLVMSetLinkage(invoker_proc->value, LLVMPrivateLinkage);
+ lb_add_function_type_attributes(invoker_proc->value, lb_get_function_type(m, invoker_proc_type), ProcCC_CDecl);
// Create the block descriptor and block literal
gbString block_lit_type_name = gb_string_make(temporary_allocator(), "__$ObjC_Block_Literal_");
@@ -2531,45 +2533,66 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
/// Invoker body
lb_begin_procedure_body(invoker_proc);
{
- auto call_args = array_make<lbValue>(temporary_allocator(), user_proc.param_count, user_proc.param_count);
+ // Reserve 2 extra arguments for: Indirect return values and context.
+ auto call_args = array_make<LLVMValueRef>(temporary_allocator(), 0, user_proc.param_count + 2);
- for (isize i = 1; i < invoker_proc->raw_input_parameters.count; i++) {
- lbValue arg = {};
- arg.type = invoker_args[i];
- arg.value = invoker_proc->raw_input_parameters[i],
- call_args[i-1] = arg;
- }
+ isize block_literal_arg_index = 0;
- LLVMValueRef block_literal = invoker_proc->raw_input_parameters[0];
+ lbFunctionType* user_proc_ft = lb_get_function_type(m, user_proc_value.type);
- // Push context, if needed
- if (user_proc.calling_convention == ProcCC_Odin) {
- LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context");
- lbValue ctx_val = {};
- ctx_val.type = t_context_ptr;
- ctx_val.value = p_context;
+ lbArgKind return_kind = {};
+
+ GB_ASSERT(user_proc.result_count <= 1);
+ if (user_proc.result_count > 0) {
+ return_kind = user_proc_ft->ret.kind;
+
+ if (return_kind == lbArg_Indirect) {
+ // Forward indirect return value
+ array_add(&call_args, invoker_proc->raw_input_parameters[0]);
+ block_literal_arg_index = 1;
+ }
+ }
- lb_push_context_onto_stack(invoker_proc, lb_addr(ctx_val));
+ // Forward raw arguments
+ for (isize i = block_literal_arg_index+1; i < invoker_proc->raw_input_parameters.count; i++) {
+ array_add(&call_args, invoker_proc->raw_input_parameters[i]);
}
+ LLVMValueRef block_literal = invoker_proc->raw_input_parameters[block_literal_arg_index];
+
// Copy capture parameters from the block literal
+ isize capture_arg_in_user_proc_start_index = user_proc_ft->args.count - capture_arg_count;
+ if (user_proc.calling_convention == ProcCC_Odin) {
+ capture_arg_in_user_proc_start_index -= 1;
+ }
+
for (isize i = 0; i < capture_arg_count; i++) {
LLVMValueRef cap_value = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, unsigned(capture_fields_offset + i), "");
- lbValue cap_arg = {};
- cap_arg.value = cap_value;
- cap_arg.type = alloc_type_pointer(captured_values[i].type);
+ // Don't emit load if indirect. Pass the pointer as-is
+ isize cap_arg_index_in_user_proc = capture_arg_in_user_proc_start_index + i;
- lbValue arg = lb_emit_load(invoker_proc, cap_arg);
- call_args[block_forward_args+i] = arg;
+ if (user_proc_ft->args[cap_arg_index_in_user_proc].kind != lbArg_Indirect) {
+ cap_value = OdinLLVMBuildLoad(invoker_proc, lb_type(invoker_proc->module, captured_values[i].type), cap_value);
+ }
+
+ array_add(&call_args, cap_value);
}
- lbValue result = lb_emit_call(invoker_proc, user_proc_value, call_args, proc_lit->ProcLit.inlining);
+ // Push context, if needed
+ if (user_proc.calling_convention == ProcCC_Odin) {
+ LLVMValueRef p_context = LLVMBuildStructGEP2(invoker_proc->builder, block_lit_type, block_literal, 5, "context");
+ array_add(&call_args, p_context);
+ }
- GB_ASSERT(user_proc.result_count <= 1);
- if (user_proc.result_count > 0) {
- GB_ASSERT(result.value != nullptr);
- LLVMBuildRet(p->builder, result.value);
+ LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(m, user_proc_value.type);
+ LLVMValueRef ret_val = LLVMBuildCall2(invoker_proc->builder, fnp, user_proc_value.value, call_args.data, (unsigned)call_args.count, "");
+
+ if (user_proc.result_count > 0 && return_kind != lbArg_Indirect) {
+ LLVMBuildRet(invoker_proc->builder, ret_val);
+ }
+ else {
+ LLVMBuildRetVoid(invoker_proc->builder);
}
}
lb_end_procedure_body(invoker_proc);
@@ -2587,8 +2610,8 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
gbString block_var_name = gb_string_make(temporary_allocator(), "__$objc_block_literal_");
block_var_name = gb_string_append_fmt(block_var_name, "%lld", m->objc_next_block_id);
- lbValue result = {};
- result.type = block_result_type;
+ lbValue block_result = {};
+ block_result.type = block_result_type;
lbValue isa_val = lb_find_runtime_value(m, is_global ? str_lit("_NSConcreteGlobalBlock") : str_lit("_NSConcreteStackBlock"));
lbValue flags_val = lb_const_int(m, t_i32, (u64)raw_flags);
@@ -2596,7 +2619,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
if (is_global) {
LLVMValueRef p_block_lit = LLVMAddGlobal(m->mod, block_lit_type, block_var_name);
- result.value = p_block_lit;
+ block_result.value = p_block_lit;
LLVMValueRef fields_values[5] = {
isa_val.value, // isa
@@ -2611,7 +2634,7 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
} else {
LLVMValueRef p_block_lit = llvm_alloca(p, block_lit_type, lb_alignof(block_lit_type), block_var_name);
- result.value = p_block_lit;
+ block_result.value = p_block_lit;
// Initialize it
LLVMValueRef f_isa = LLVMBuildStructGEP2(p->builder, block_lit_type, p_block_lit, 0, "isa");
@@ -2651,7 +2674,20 @@ gb_internal lbValue lb_handle_objc_block(lbProcedure *p, Ast *expr) {
}
}
- return result;
+ return block_result;
+}
+
+gb_internal lbValue lb_handle_objc_block_invoke(lbProcedure *p, Ast *expr) {
+ return {};
+}
+
+gb_internal lbValue lb_handle_objc_super(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+ GB_ASSERT(ce->args.count == 1);
+
+ // NOTE(harold): This doesn't actually do anything by itself,
+ // it has an effect when used on the left side of a selector call expression.
+ return lb_build_expr(p, ce->args[0]);
}
gb_internal lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
@@ -2767,6 +2803,120 @@ gb_internal lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
return lb_emit_call(p, the_proc, args);
}
+gb_internal lbValue lb_handle_objc_auto_send(lbProcedure *p, Ast *expr, Slice<lbValue> const arg_values) {
+ ast_node(ce, CallExpr, expr);
+
+ lbModule *m = p->module;
+ CheckerInfo *info = m->info;
+ ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr);
+
+ Type *proc_type = data.proc_type;
+ GB_ASSERT(proc_type != nullptr);
+
+ Entity *objc_method_ent = entity_of_node(ce->proc);
+ GB_ASSERT(objc_method_ent != nullptr);
+ GB_ASSERT(objc_method_ent->kind == Entity_Procedure);
+ GB_ASSERT(objc_method_ent->Procedure.objc_selector_name.len > 0);
+
+ auto &proc = proc_type->Proc;
+ GB_ASSERT(proc.param_count >= 2);
+
+ Type *objc_super_orig_type = nullptr;
+ if (ce->args.count > 0) {
+ objc_super_orig_type = unparen_expr(ce->args[0])->tav.objc_super_target;
+ }
+
+ isize arg_offset = 1;
+ lbValue id = {};
+ if (!objc_method_ent->Procedure.is_objc_class_method) {
+ GB_ASSERT(ce->args.count > 0);
+ id = arg_values[0];
+
+ if (objc_super_orig_type) {
+ GB_ASSERT(objc_super_orig_type->kind == Type_Named);
+
+ auto& tn = objc_super_orig_type->Named.type_name->TypeName;
+ lbAddr p_supercls = lb_handle_objc_find_or_register_class(p, tn.objc_class_name, tn.objc_is_implementation ? objc_super_orig_type : nullptr);
+
+ lbValue supercls = lb_addr_load(p, p_supercls);
+ lbAddr p_objc_super = lb_add_local_generated(p, t_objc_super, false);
+
+ lbValue f_id = lb_emit_struct_ep(p, p_objc_super.addr, 0);
+ lbValue f_superclass = lb_emit_struct_ep(p, p_objc_super.addr, 1);
+
+ id = lb_emit_conv(p, id, t_objc_id);
+ lb_emit_store(p, f_id, id);
+ lb_emit_store(p, f_superclass, supercls);
+
+ id = p_objc_super.addr;
+ }
+ } else {
+ Entity *objc_class = objc_method_ent->Procedure.objc_class;
+ if (ce->proc->kind == Ast_SelectorExpr) {
+ // NOTE (harold): If called via a selector expression (ex: Foo.alloc()), then we should use
+ // the lhs-side to determine the class. This allows for class methods to be called
+ // with the correct class as the target, even when the method is defined in a superclass.
+ ast_node(se, SelectorExpr, ce->proc);
+ GB_ASSERT(se->expr->tav.mode == Addressing_Type && se->expr->tav.type->kind == Type_Named);
+
+ objc_class = entity_from_expr(se->expr);
+
+ GB_ASSERT(objc_class);
+ GB_ASSERT(objc_class->kind == Entity_TypeName);
+ GB_ASSERT(objc_class->TypeName.objc_class_name != "");
+ }
+
+ Type *class_impl_type = objc_class->TypeName.objc_is_implementation ? objc_class->type : nullptr;
+
+ id = lb_addr_load(p, lb_handle_objc_find_or_register_class(p, objc_class->TypeName.objc_class_name, class_impl_type));
+ arg_offset = 0;
+ }
+
+ lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, objc_method_ent->Procedure.objc_selector_name));
+
+ auto args = array_make<lbValue>(permanent_allocator(), 0, arg_values.count + 2 - arg_offset);
+
+ array_add(&args, id);
+ array_add(&args, sel);
+
+ for (isize i = arg_offset; i < ce->args.count; i++) {
+ array_add(&args, arg_values[i]);
+ }
+
+ lbValue the_proc = {};
+
+ if (!objc_super_orig_type) {
+ switch (data.kind) {
+ default:
+ GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
+ break;
+ case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break;
+ case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break;
+ case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break;
+ case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break;
+ }
+ } else {
+ switch (data.kind) {
+ default:
+ GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
+ break;
+ case ObjcMsg_normal:
+ case ObjcMsg_fpret:
+ case ObjcMsg_fp2ret:
+ the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSendSuper2"));
+ break;
+ case ObjcMsg_stret:
+ the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSendSuper2_stret"));
+ break;
+ }
+ }
+
+ the_proc = lb_emit_conv(p, the_proc, data.proc_type);
+
+ return lb_emit_call(p, the_proc, args);
+}
+
+
gb_internal LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
GB_ASSERT(value.kind == ExactValue_Integer);
i64 v = exact_value_to_i64(value);
diff --git a/src/parser.hpp b/src/parser.hpp
index 979b44618..6127468d4 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -48,7 +48,8 @@ gb_global String const addressing_mode_strings[] = {
struct TypeAndValue {
Type * type;
AddressingMode mode;
- bool is_lhs; // Debug info
+ bool is_lhs; // Debug info
+ Type * objc_super_target; // Original type of the Obj-C object before being converted to the superclass' type by the objc_super() intrinsic.
ExactValue value;
};
diff --git a/src/types.cpp b/src/types.cpp
index effa8ef64..372c2e991 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -752,11 +752,14 @@ gb_global Type *t_objc_object = nullptr;
gb_global Type *t_objc_selector = nullptr;
gb_global Type *t_objc_class = nullptr;
gb_global Type *t_objc_ivar = nullptr;
+gb_global Type *t_objc_super = nullptr; // Struct used in lieu of the 'self' instance when calling objc_msgSendSuper.
+gb_global Type *t_objc_super_ptr = nullptr;
gb_global Type *t_objc_id = nullptr;
gb_global Type *t_objc_SEL = nullptr;
gb_global Type *t_objc_Class = nullptr;
gb_global Type *t_objc_Ivar = nullptr;
+gb_global Type *t_objc_instancetype = nullptr; // Special distinct variant of t_objc_id used mimic auto-typing of instancetype* in Objective-C
enum OdinAtomicMemoryOrder : i32 {
OdinAtomicMemoryOrder_relaxed = 0, // unordered
@@ -4735,6 +4738,14 @@ gb_internal bool is_type_objc_object(Type *t) {
return internal_check_is_assignable_to(t, t_objc_object);
}
+gb_internal bool is_type_objc_ptr_to_object(Type *t) {
+ // NOTE (harold): is_type_objc_object() returns true if it's a pointer to an object or the object itself.
+ // This returns true ONLY if Type is a shallow pointer to an Objective-C object.
+
+ Type *elem = type_deref(t);
+ return elem != t && elem->kind == Type_Named && is_type_objc_object(elem);
+}
+
gb_internal Type *get_struct_field_type(Type *t, isize index) {
t = base_type(type_deref(t));
GB_ASSERT(t->kind == Type_Struct);