Automatically emit objc_msgSend calls when calling imported or implemented Objective-C methods

- Add intrinsics.objc_super()

- Emit objc_msgSendSuper2 calls when an objc method call is combined with objc_super(self)

- Fix objc_block return value ABI for large struct returns

- Fix objc_implement method wrappers bad ABI for large struct returns and indirect args

- Simplify parameter forwarding for objc_imlpement methods

- Add intrinsics.objc_instancetype to mimi Objective-C instancetype* returns
  This facilitates returning the correct type on subclasses when calling mehtods
  such as `alloc`, `init`, `retain`, etc.

- Refactor Objective-C class implementations generation so that hierarchies are properly initialized

- Better codegen for context passing with ivar-based autocontext

- Allow @superclass on imported objc-c objects

- Better codegen for block forwarding invoker, arguments are forwarded directly
This commit is contained in:
Harold Brenes
2025-09-16 00:49:31 -04:00
parent 9b4c0ea492
commit 5af13f5d53
13 changed files with 575 additions and 151 deletions

View File

@@ -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: