Просмотр исходного кода

Fix indentations
Fix Objective-C wrapper procs not forwarding return value

Harold Brenes 4 месяцев назад
Родитель
Сommit
f3923ed666

+ 72 - 72
src/check_builtin.cpp

@@ -389,77 +389,77 @@ gb_internal bool check_builtin_objc_procedure(CheckerContext *c, Operand *operan
 	} break;
 
 	case BuiltinProc_objc_ivar_get:
-    {
-        Type *self_type = nullptr;
-        Type *ivar_type = nullptr;
-
-        Operand self = {};
-        check_expr_or_type(c, &self, ce->args[0]);
-
-        if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
-            gbString e = expr_to_string(self.expr);
-            gbString t = type_to_string(self.type);
-            error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
-            gb_string_free(t);
-            gb_string_free(e);
-            return false;
-        } else if (!is_type_pointer(self.type)) {
-            gbString e = expr_to_string(self.expr);
-            gbString t = type_to_string(self.type);
-            error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
-            gb_string_free(t);
-            gb_string_free(e);
-            return false;
-        }
-
-        self_type = type_deref(self.type);
-
-        if (!(self_type->kind == Type_Named &&
-            self_type->Named.type_name != nullptr &&
-                self_type->Named.type_name->TypeName.objc_class_name != "")) {
-            gbString t = type_to_string(self_type);
-            error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=<string>) , got type %s", LIT(builtin_name), t);
-            gb_string_free(t);
-            return false;
-        }
-
-        if (self_type->Named.type_name->TypeName.objc_ivar == nullptr) {
-            gbString t = type_to_string(self_type);
-            error(self.expr, "'%.*s' requires that type %s have the attribute @(obj_ivar=<ivar_type_name>).", LIT(builtin_name), t);
-            gb_string_free(t);
-            return false;
-        }
-
-        Operand ivar = {};
-        check_expr_or_type(c, &ivar, ce->args[1]);
-        if (ivar.mode == Addressing_Type) {
-            ivar_type = ivar.type;
-        } else {
-            return false;
-        }
-
-        if (self_type->Named.type_name->TypeName.objc_ivar != ivar_type) {
-            gbString name_self     = type_to_string(self_type);
-            gbString name_expected = type_to_string(self_type->Named.type_name->TypeName.objc_ivar);
-            gbString name_given    = type_to_string(ivar_type);
-            error(self.expr, "'%.*s' ivar type %s does not match @obj_ivar type %s on Objective-C class %s.",
-                  LIT(builtin_name), name_given, name_expected, name_self);
-            gb_string_free(name_self);
-            gb_string_free(name_expected);
-            gb_string_free(name_given);
-            return false;
-        }
-
-        if (type_hint != nullptr && type_hint->kind == Type_Pointer && type_hint->Pointer.elem == ivar_type) {
-            operand->type = type_hint;
-        } else {
-            operand->type = alloc_type_pointer(ivar_type);
-        }
-
-        operand->mode = Addressing_Value;
-
-        return true;
-    } break;
+	{
+		Type *self_type = nullptr;
+		Type *ivar_type = nullptr;
+
+		Operand self = {};
+		check_expr_or_type(c, &self, ce->args[0]);
+
+		if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
+			gbString e = expr_to_string(self.expr);
+			gbString t = type_to_string(self.type);
+			error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+			gb_string_free(t);
+			gb_string_free(e);
+			return false;
+		} else if (!is_type_pointer(self.type)) {
+			gbString e = expr_to_string(self.expr);
+			gbString t = type_to_string(self.type);
+			error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+			gb_string_free(t);
+			gb_string_free(e);
+			return false;
+		}
+
+		self_type = type_deref(self.type);
+
+		if (!(self_type->kind == Type_Named &&
+			self_type->Named.type_name != nullptr &&
+				self_type->Named.type_name->TypeName.objc_class_name != "")) {
+			gbString t = type_to_string(self_type);
+			error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=<string>) , got type %s", LIT(builtin_name), t);
+			gb_string_free(t);
+			return false;
+		}
+
+		if (self_type->Named.type_name->TypeName.objc_ivar == nullptr) {
+			gbString t = type_to_string(self_type);
+			error(self.expr, "'%.*s' requires that type %s have the attribute @(obj_ivar=<ivar_type_name>).", LIT(builtin_name), t);
+			gb_string_free(t);
+			return false;
+		}
+
+		Operand ivar = {};
+		check_expr_or_type(c, &ivar, ce->args[1]);
+		if (ivar.mode == Addressing_Type) {
+			ivar_type = ivar.type;
+		} else {
+			return false;
+		}
+
+		if (self_type->Named.type_name->TypeName.objc_ivar != ivar_type) {
+			gbString name_self     = type_to_string(self_type);
+			gbString name_expected = type_to_string(self_type->Named.type_name->TypeName.objc_ivar);
+			gbString name_given    = type_to_string(ivar_type);
+			error(self.expr, "'%.*s' ivar type %s does not match @obj_ivar type %s on Objective-C class %s.",
+				  LIT(builtin_name), name_given, name_expected, name_self);
+			gb_string_free(name_self);
+			gb_string_free(name_expected);
+			gb_string_free(name_given);
+			return false;
+		}
+
+		if (type_hint != nullptr && type_hint->kind == Type_Pointer && type_hint->Pointer.elem == ivar_type) {
+			operand->type = type_hint;
+		} else {
+			operand->type = alloc_type_pointer(ivar_type);
+		}
+
+		operand->mode = Addressing_Value;
+
+		return true;
+	} break;
 	}
 }
 
@@ -2206,7 +2206,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 	case BuiltinProc_objc_find_class: 
 	case BuiltinProc_objc_register_selector: 
 	case BuiltinProc_objc_register_class:
-    case BuiltinProc_objc_ivar_get:
+	case BuiltinProc_objc_ivar_get:
 		return check_builtin_objc_procedure(c, operand, call, id, type_hint);
 
 	case BuiltinProc___entry_point:

+ 87 - 87
src/check_decl.cpp

@@ -526,68 +526,68 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
 		check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
 		if (e->kind == Entity_TypeName && ac.objc_class != "") {
 			e->TypeName.objc_class_name = ac.objc_class;
-            e->TypeName.objc_superclass = ac.objc_superclass;
-            e->TypeName.objc_ivar       = ac.objc_ivar;
+			e->TypeName.objc_superclass = ac.objc_superclass;
+			e->TypeName.objc_ivar       = ac.objc_ivar;
 			e->TypeName.objc_context_provider = ac.objc_context_provider;
 
-            if (ac.objc_is_implementation) {
-                e->TypeName.objc_is_implementation = true;
-                mpsc_enqueue(&ctx->info->objc_class_implementations, e);    // TODO(harold): Don't need this for anything? See if needed when using explicit @export
-
-                GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named);
-
-            	// Enqueue the proc to be checked when resolved
-            	if (e->TypeName.objc_context_provider != nullptr) {
-            		mpsc_enqueue(&ctx->checker->procs_with_objc_context_provider_to_check, e);
-            	}
-
-            	// @TODO(harold): I think there's a Check elsewhere in the checker for checking cycles.
-            	//					See about moving this to the right location.
-                // Ensure superclass hierarchy are all Objective-C classes and does not cycle
-                Type *super = ac.objc_superclass;
-                if (super != nullptr) {
-                    TypeSet super_set{};
-                    type_set_init(&super_set, 8);
-                    defer (type_set_destroy(&super_set));
-
-                    type_set_update(&super_set, e->type);
-
-                    for (;;) {
-                        if (type_set_update(&super_set, super)) {
-                            error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered");
-                            break;
-                        }
-
-                        if (super->kind != Type_Named) {
-                            error(e->token, "@(objc_superclass) References type must be a named struct.");
-                            break;
-                        }
-
-                        Type* named_type = base_type(super->Named.type_name->type);
-                        if (!is_type_objc_object(named_type)) {
-                            error(e->token, "@(objc_superclass) Superclass must be an Objective-C class.");
-                            break;
-                        }
-
-                        super = super->Named.type_name->TypeName.objc_superclass;
-                        if (super == nullptr) {
-                            break;
-                        }
-
-                        // TODO(harold): Is this the right way to do this??? The referenced entity must be already resolved
-                        //               so that we can access its objc_superclass attribute
-                        check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info);
-                    }
-                }
-            } else {
-            	if (e->TypeName.objc_superclass != nullptr) {
-            		error(e->token, "@(objc_superclass) can only be applied when the @(obj_implement) attribute is also applied");
-            	} else if (e->TypeName.objc_ivar != nullptr) {
-            		error(e->token, "@(objc_ivar) can only be applied when the @(obj_implement) attribute is also applied");
-            	} else if (e->TypeName.objc_context_provider != nullptr) {
-            		error(e->token, "@(objc_context_provider) can only be applied when the @(obj_implement) attribute is also applied");
-            	}
-            }
+			if (ac.objc_is_implementation) {
+				e->TypeName.objc_is_implementation = true;
+				mpsc_enqueue(&ctx->info->objc_class_implementations, e);    // TODO(harold): Don't need this for anything? See if needed when using explicit @export
+
+				GB_ASSERT(e->TypeName.objc_ivar == nullptr || e->TypeName.objc_ivar->kind == Type_Named);
+
+				// Enqueue the proc to be checked when resolved
+				if (e->TypeName.objc_context_provider != nullptr) {
+					mpsc_enqueue(&ctx->checker->procs_with_objc_context_provider_to_check, e);
+				}
+
+				// @TODO(harold): I think there's a Check elsewhere in the checker for checking cycles.
+				//					See about moving this to the right location.
+				// Ensure superclass hierarchy are all Objective-C classes and does not cycle
+				Type *super = ac.objc_superclass;
+				if (super != nullptr) {
+					TypeSet super_set{};
+					type_set_init(&super_set, 8);
+					defer (type_set_destroy(&super_set));
+
+					type_set_update(&super_set, e->type);
+
+					for (;;) {
+						if (type_set_update(&super_set, super)) {
+							error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered");
+							break;
+						}
+
+						if (super->kind != Type_Named) {
+							error(e->token, "@(objc_superclass) References type must be a named struct.");
+							break;
+						}
+
+						Type* named_type = base_type(super->Named.type_name->type);
+						if (!is_type_objc_object(named_type)) {
+							error(e->token, "@(objc_superclass) Superclass must be an Objective-C class.");
+							break;
+						}
+
+						super = super->Named.type_name->TypeName.objc_superclass;
+						if (super == nullptr) {
+							break;
+						}
+
+						// TODO(harold): Is this the right way to do this??? The referenced entity must be already resolved
+						//               so that we can access its objc_superclass attribute
+						check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info);
+					}
+				}
+			} else {
+				if (e->TypeName.objc_superclass != nullptr) {
+					error(e->token, "@(objc_superclass) can only be applied when the @(obj_implement) attribute is also applied");
+				} else if (e->TypeName.objc_ivar != nullptr) {
+					error(e->token, "@(objc_ivar) can only be applied when the @(obj_implement) attribute is also applied");
+				} else if (e->TypeName.objc_context_provider != nullptr) {
+					error(e->token, "@(objc_context_provider) can only be applied when the @(obj_implement) attribute is also applied");
+				}
+			}
 
 			if (type_size_of(e->type) > 0) {
 				error(e->token, "@(objc_class) marked type must be of zero size");
@@ -1005,37 +1005,37 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
 				error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
 			} else {
 
-                if (ac.objc_is_implementation) {
-                    GB_ASSERT(e->kind == Entity_Procedure);
+				if (ac.objc_is_implementation) {
+					GB_ASSERT(e->kind == Entity_Procedure);
 
-                	Type *proc_type = e->type;
+					Type *proc_type = e->type;
 
-                	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 (proc_type->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_type->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 (!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 (proc_type->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_type->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 {
 
 	                    auto method = ObjcMethodData{ ac, e };
-                		method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
-
-	                    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;
-
-	                        map_set(&info->objc_method_implementations, t, list);
-	                    }
-                	}
-                }
+						method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name;
+
+						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;
+
+							map_set(&info->objc_method_implementations, t, list);
+						}
+					}
+				}
 
 				mutex_lock(&global_type_name_objc_metadata_mutex);
 				defer (mutex_unlock(&global_type_name_objc_metadata_mutex));

+ 75 - 75
src/checker.cpp

@@ -1351,12 +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_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);
+		t_objc_Ivar     = alloc_type_pointer(t_objc_ivar);
 	}
 }
 
@@ -1389,8 +1389,8 @@ 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);
-    map_init(&i->objc_method_implementations);
+	mpsc_init(&i->objc_class_implementations, a);
+	map_init(&i->objc_method_implementations);
 
 	string_map_init(&i->load_file_cache);
 	array_init(&i->all_procedures, heap_allocator());
@@ -3352,10 +3352,10 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		ac->test = true;
 		return true;
 	} else if (name == "export") {
-        if (ac->objc_is_implementation) {
-            error(value, "Setting @(export) explicitly is not allowed when @(objc_implement) is set. It is exported implicitly.");
-            return false;
-        }
+		if (ac->objc_is_implementation) {
+			error(value, "Setting @(export) explicitly is not allowed when @(objc_implement) is set. It is exported implicitly.");
+			return false;
+		}
 
 		ExactValue ev = check_decl_attribute_value(c, value);
 		if (ev.kind == ExactValue_Invalid) {
@@ -3369,10 +3369,10 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		return true;
 	} else if (name == "linkage") {
 
-        if (ac->objc_is_implementation) {
-            error(value, "Explicit linkage not allowed when @(objc_implement) is set. It is set implicitly");
-            return false;
-        }
+		if (ac->objc_is_implementation) {
+			error(value, "Explicit linkage not allowed when @(objc_implement) is set. It is set implicitly");
+			return false;
+		}
 
 		ExactValue ev = check_decl_attribute_value(c, value);
 		if (ev.kind != ExactValue_String) {
@@ -3681,23 +3681,23 @@ 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;
-        } 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));
-        }
-
-        // This implies exported, strongly linked
-        if (ac->objc_is_implementation) {
-            ac->is_export = true;
-            ac->linkage   = str_lit("strong");
-        }
-
-        return true;
-    } else if (name == "objc_selector") {
+		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));
+		}
+
+		// This implies exported, strongly linked
+		if (ac->objc_is_implementation) {
+			ac->is_export = true;
+			ac->linkage   = str_lit("strong");
+		}
+
+		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)) {
@@ -3949,52 +3949,52 @@ gb_internal DECL_ATTRIBUTE_PROC(type_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;
-        } 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) {
-            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;
+		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) {
+			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;
 }
 
 

+ 9 - 9
src/checker.hpp

@@ -148,13 +148,13 @@ struct AttributeContext {
 
 	String  objc_class;
 	String  objc_name;
-    String  objc_selector;
+	String  objc_selector;
 	Type *  objc_type;
-    Type *  objc_superclass;
-    Type *  objc_ivar;
+	Type *  objc_superclass;
+	Type *  objc_ivar;
 	Entity *objc_context_provider;
 	bool    objc_is_class_method   : 1;
-    bool    objc_is_implementation : 1;     // This struct or proc provides a class/method implementation, not a binding to an existing type.
+	bool    objc_is_implementation : 1;     // This struct or proc provides a class/method implementation, not a binding to an existing type.
 
 	String require_target_feature; // required by the target micro-architecture
 	String enable_target_feature;  // will be enabled for the procedure only
@@ -371,8 +371,8 @@ struct ObjcMsgData {
 };
 
 struct ObjcMethodData {
-    AttributeContext ac;
-    Entity *proc_entity;
+	AttributeContext ac;
+	Entity *proc_entity;
 };
 
 enum LoadFileTier {
@@ -489,10 +489,10 @@ struct CheckerInfo {
 	BlockingMutex objc_types_mutex;
 	PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
 
-    MPSCQueue<Entity *> objc_class_implementations;
+	MPSCQueue<Entity *> objc_class_implementations;
 
-    BlockingMutex objc_method_mutex;
-    PtrMap<Type *, Array<ObjcMethodData>> objc_method_implementations;
+	BlockingMutex objc_method_mutex;
+	PtrMap<Type *, Array<ObjcMethodData>> objc_method_implementations;
 
 
 	BlockingMutex load_file_mutex;

+ 2 - 2
src/checker_builtin_procs.hpp

@@ -331,7 +331,7 @@ BuiltinProc__type_end,
 	BuiltinProc_objc_find_class,
 	BuiltinProc_objc_register_selector,
 	BuiltinProc_objc_register_class,
-    BuiltinProc_objc_ivar_get,
+	BuiltinProc_objc_ivar_get,
 
 	BuiltinProc_constant_utf16_cstring,
 
@@ -674,7 +674,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("objc_find_class"),        1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{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("ivar_get"),               2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+	{STR_LIT("ivar_get"),               2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
 
 	{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 

+ 3 - 3
src/entity.cpp

@@ -235,9 +235,9 @@ struct Entity {
 			Type * type_parameter_specialization;
 			String ir_mangled_name;
 			bool   is_type_alias;
-            bool   objc_is_implementation;
-            Type*  objc_superclass;
-            Type*  objc_ivar;
+			bool   objc_is_implementation;
+			Type*  objc_superclass;
+			Type*  objc_ivar;
 			Entity*objc_context_provider;
 			String objc_class_name;
 			TypeNameObjCMetadata *objc_metadata;

+ 561 - 547
src/llvm_backend.cpp

@@ -1176,327 +1176,327 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
 // TODO(harold): Move this out of here and into a more suitable place.
 // TODO(harold): Should not take an allocator, but always use temp, as we return string literals as well.
 String lb_get_objc_type_encoding(Type *t, gbAllocator allocator, isize pointer_depth = 0) {
-    // NOTE(harold): See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
-
-    // NOTE(harold): Darwin targets are always 64-bit. Should we drop this and assume "q" always?
-    #define INT_SIZE_ENCODING (build_context.metrics.ptr_size == 4 ? "i" : "q")
-    switch (t->kind) {
-    case Type_Basic: {
-        switch (t->Basic.kind) {
-        case Basic_Invalid:
-            return str_lit("?");
-
-        case Basic_llvm_bool:
-        case Basic_bool:
-        case Basic_b8:
-            return str_lit("B");
-
-        case Basic_b16:
-            return str_lit("C");
-        case Basic_b32:
-            return str_lit("I");
-        case Basic_b64:
-            return str_lit("q");
-        case Basic_i8:
-            return str_lit("c");
-        case Basic_u8:
-            return str_lit("C");
-        case Basic_i16:
-        case Basic_i16le:
-        case Basic_i16be:
-            return str_lit("s");
-        case Basic_u16:
-        case Basic_u16le:
-        case Basic_u16be:
-            return str_lit("S");
-        case Basic_i32:
-        case Basic_i32le:
-        case Basic_i32be:
-            return str_lit("i");
-        case Basic_u32le:
-        case Basic_u32:
-        case Basic_u32be:
-            return str_lit("I");
-        case Basic_i64:
-        case Basic_i64le:
-        case Basic_i64be:
-            return str_lit("q");
-        case Basic_u64:
-        case Basic_u64le:
-        case Basic_u64be:
-            return str_lit("Q");
-        case Basic_i128:
-        case Basic_i128le:
-        case Basic_i128be:
-            return str_lit("t");
-        case Basic_u128:
-        case Basic_u128le:
-        case Basic_u128be:
-            return str_lit("T");
-        case Basic_rune:
-            return str_lit("I");
-        case Basic_f16:
-        case Basic_f16le:
-        case Basic_f16be:
-            return str_lit("s");    // @harold: Closest we've got?
-        case Basic_f32:
-        case Basic_f32le:
-        case Basic_f32be:
-            return str_lit("f");
-        case Basic_f64:
-        case Basic_f64le:
-        case Basic_f64be:
-            return str_lit("d");
-
-        // TODO(harold) These:
-        case Basic_complex32:
-        case Basic_complex64:
-        case Basic_complex128:
-        case Basic_quaternion64:
-        case Basic_quaternion128:
-        case Basic_quaternion256:
-                return str_lit("?");
-
-        case Basic_int:
-            return str_lit(INT_SIZE_ENCODING);
-        case Basic_uint:
-            return build_context.metrics.ptr_size == 4 ? str_lit("I") : str_lit("Q");
-        case Basic_uintptr:
-        case Basic_rawptr:
-            return str_lit("^v");
-
-        case Basic_string:
-            return build_context.metrics.ptr_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}");
-
-        case Basic_cstring: return str_lit("*");
-        case Basic_any:     return str_lit("{any=^v^v");  // rawptr + ^Type_Info
-
-        case Basic_typeid:
-            GB_ASSERT(t->Basic.size == 8);
-            return str_lit("q");
-
-        // Untyped types
-        case Basic_UntypedBool:
-        case Basic_UntypedInteger:
-        case Basic_UntypedFloat:
-        case Basic_UntypedComplex:
-        case Basic_UntypedQuaternion:
-        case Basic_UntypedString:
-        case Basic_UntypedRune:
-        case Basic_UntypedNil:
-        case Basic_UntypedUninit:
-            GB_PANIC("Untyped types cannot be @encoded()");
-            return str_lit("?");
-        }
-        break;
-    }
-
-    case Type_Named:
-    case Type_Struct:
-    case Type_Union: {
-        Type* base = t;
-        if (base->kind == Type_Named) {
-            base = base_type(base);
-            if(base->kind != Type_Struct && base->kind != Type_Union) {
-                return lb_get_objc_type_encoding(base, allocator, pointer_depth);
-            }
-        }
-
-        const bool is_union = base->kind == Type_Union;
-        if (!is_union) {
-            // Check for objc_SEL
-            if (internal_check_is_assignable_to(base, t_objc_SEL)) {
-                return str_lit(":");
-            }
-
-            // Check for objc_Class
-            if (internal_check_is_assignable_to(base, t_objc_SEL)) {
-                return str_lit("#");
-            }
-
-            // Treat struct as an Objective-C Class?
-            if (has_type_got_objc_class_attribute(base) && pointer_depth == 0) {
-                return str_lit("#");
-            }
-        }
-
-        if (is_type_objc_object(base)) {
-            return str_lit("@");
-        }
-
-
-        gbString s = gb_string_make_reserve(allocator, 16);
-        s = gb_string_append_length(s, is_union ? "(" :"{", 1);
-        if (t->kind == Type_Named) {
-            s = gb_string_append_length(s, t->Named.name.text, t->Named.name.len);
-        }
-
-        // Write fields
-        if (pointer_depth < 2) {
-            s = gb_string_append_length(s, "=", 1);
-
-            if (!is_union) {
-                for( auto& f : base->Struct.fields ) {
-                    String field_type = lb_get_objc_type_encoding(f->type, allocator, pointer_depth);
-                    s = gb_string_append_length(s, field_type.text, field_type.len);
-                }
-            } else {
-                // #TODO(harold): Encode fields
-            }
-        }
-
-        s = gb_string_append_length(s, is_union ? ")" :"}", 1);
-
-        return make_string_c(s);
-    }
-
-    case Type_Generic:
-        GB_PANIC("Generic types cannot be @encoded()");
-        return str_lit("?");
-
-    case Type_Pointer: {
-        String pointee = lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1);
-        // Special case for Objective-C Objects
-        if (pointer_depth == 0 && pointee == "@") {
-            return pointee;
-        }
-
-        return concatenate_strings(allocator, str_lit("^"), pointee);
-    }
-
-    case Type_MultiPointer:
-        return concatenate_strings(allocator, str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1));
-
-    case Type_Array: {
-        String type_str = lb_get_objc_type_encoding(t->Array.elem, allocator, pointer_depth);
-
-        gbString s = gb_string_make_reserve(allocator, type_str.len + 8);
-        s = gb_string_append_fmt(s, "[%lld%s]", t->Array.count, type_str.text);
-        return make_string_c(s);
-    }
-
-    case Type_EnumeratedArray: {
-        String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, allocator, pointer_depth);
-
-        gbString s = gb_string_make_reserve(allocator, type_str.len + 8);
-        s = gb_string_append_fmt(s, "[%lld%s]", t->EnumeratedArray.count, type_str.text);
-        return make_string_c(s);
-    }
-
-    case Type_Slice: {
-        String type_str = lb_get_objc_type_encoding(t->Slice.elem, allocator, pointer_depth);
-        gbString s = gb_string_make_reserve(allocator, type_str.len + 8);
-        s = gb_string_append_fmt(s, "{slice=^%s%s}", type_str, INT_SIZE_ENCODING);
-        return make_string_c(s);
-    }
-
-    case Type_DynamicArray: {
-        String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, allocator, pointer_depth);
-        gbString s = gb_string_make_reserve(allocator, type_str.len + 8);
-        s = gb_string_append_fmt(s, "{dynamic=^%s%s%sAllocator={?^v}}", type_str, INT_SIZE_ENCODING, INT_SIZE_ENCODING);
-        return make_string_c(s);
-    }
-
-    case Type_Map:
-        return str_lit("{^v^v{Allocator=?^v}}");
-    case Type_Enum:
-        return lb_get_objc_type_encoding(t->Enum.base_type, allocator, pointer_depth);
-    case Type_Tuple:
-        // NOTE(harold): Is this allowed here?
-        return str_lit("?");
-    case Type_Proc:
-        return str_lit("?");
-    case Type_BitSet:
-        return lb_get_objc_type_encoding(t->BitSet.underlying, allocator, pointer_depth);
-    case Type_SimdVector:
-        break;
-    case Type_Matrix:
-        break;
-    case Type_BitField:
-        return lb_get_objc_type_encoding(t->BitField.backing_type, allocator, pointer_depth);
-    case Type_SoaPointer: {
-        gbString s = gb_string_make_reserve(allocator, 8);
-        s = gb_string_append_fmt(s, "{=^v%s}", INT_SIZE_ENCODING);
-        return make_string_c(s);
-    }
-
-    } // End switch t->kind
-    #undef INT_SIZE_ENCODING
-
-    GB_PANIC("Unreachable");
+	// NOTE(harold): See https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
+
+	// NOTE(harold): Darwin targets are always 64-bit. Should we drop this and assume "q" always?
+	#define INT_SIZE_ENCODING (build_context.metrics.ptr_size == 4 ? "i" : "q")
+	switch (t->kind) {
+	case Type_Basic: {
+		switch (t->Basic.kind) {
+		case Basic_Invalid:
+			return str_lit("?");
+
+		case Basic_llvm_bool:
+		case Basic_bool:
+		case Basic_b8:
+			return str_lit("B");
+
+		case Basic_b16:
+			return str_lit("C");
+		case Basic_b32:
+			return str_lit("I");
+		case Basic_b64:
+			return str_lit("q");
+		case Basic_i8:
+			return str_lit("c");
+		case Basic_u8:
+			return str_lit("C");
+		case Basic_i16:
+		case Basic_i16le:
+		case Basic_i16be:
+			return str_lit("s");
+		case Basic_u16:
+		case Basic_u16le:
+		case Basic_u16be:
+			return str_lit("S");
+		case Basic_i32:
+		case Basic_i32le:
+		case Basic_i32be:
+			return str_lit("i");
+		case Basic_u32le:
+		case Basic_u32:
+		case Basic_u32be:
+			return str_lit("I");
+		case Basic_i64:
+		case Basic_i64le:
+		case Basic_i64be:
+			return str_lit("q");
+		case Basic_u64:
+		case Basic_u64le:
+		case Basic_u64be:
+			return str_lit("Q");
+		case Basic_i128:
+		case Basic_i128le:
+		case Basic_i128be:
+			return str_lit("t");
+		case Basic_u128:
+		case Basic_u128le:
+		case Basic_u128be:
+			return str_lit("T");
+		case Basic_rune:
+			return str_lit("I");
+		case Basic_f16:
+		case Basic_f16le:
+		case Basic_f16be:
+			return str_lit("s");    // @harold: Closest we've got?
+		case Basic_f32:
+		case Basic_f32le:
+		case Basic_f32be:
+			return str_lit("f");
+		case Basic_f64:
+		case Basic_f64le:
+		case Basic_f64be:
+			return str_lit("d");
+
+		// TODO(harold) These:
+		case Basic_complex32:
+		case Basic_complex64:
+		case Basic_complex128:
+		case Basic_quaternion64:
+		case Basic_quaternion128:
+		case Basic_quaternion256:
+				return str_lit("?");
+
+		case Basic_int:
+			return str_lit(INT_SIZE_ENCODING);
+		case Basic_uint:
+			return build_context.metrics.ptr_size == 4 ? str_lit("I") : str_lit("Q");
+		case Basic_uintptr:
+		case Basic_rawptr:
+			return str_lit("^v");
+
+		case Basic_string:
+			return build_context.metrics.ptr_size == 4 ? str_lit("{string=*i}") : str_lit("{string=*q}");
+
+		case Basic_cstring: return str_lit("*");
+		case Basic_any:     return str_lit("{any=^v^v");  // rawptr + ^Type_Info
+
+		case Basic_typeid:
+			GB_ASSERT(t->Basic.size == 8);
+			return str_lit("q");
+
+		// Untyped types
+		case Basic_UntypedBool:
+		case Basic_UntypedInteger:
+		case Basic_UntypedFloat:
+		case Basic_UntypedComplex:
+		case Basic_UntypedQuaternion:
+		case Basic_UntypedString:
+		case Basic_UntypedRune:
+		case Basic_UntypedNil:
+		case Basic_UntypedUninit:
+			GB_PANIC("Untyped types cannot be @encoded()");
+			return str_lit("?");
+		}
+		break;
+	}
+
+	case Type_Named:
+	case Type_Struct:
+	case Type_Union: {
+		Type* base = t;
+		if (base->kind == Type_Named) {
+			base = base_type(base);
+			if(base->kind != Type_Struct && base->kind != Type_Union) {
+				return lb_get_objc_type_encoding(base, allocator, pointer_depth);
+			}
+		}
+
+		const bool is_union = base->kind == Type_Union;
+		if (!is_union) {
+			// Check for objc_SEL
+			if (internal_check_is_assignable_to(base, t_objc_SEL)) {
+				return str_lit(":");
+			}
+
+			// Check for objc_Class
+			if (internal_check_is_assignable_to(base, t_objc_SEL)) {
+				return str_lit("#");
+			}
+
+			// Treat struct as an Objective-C Class?
+			if (has_type_got_objc_class_attribute(base) && pointer_depth == 0) {
+				return str_lit("#");
+			}
+		}
+
+		if (is_type_objc_object(base)) {
+			return str_lit("@");
+		}
+
+
+		gbString s = gb_string_make_reserve(allocator, 16);
+		s = gb_string_append_length(s, is_union ? "(" :"{", 1);
+		if (t->kind == Type_Named) {
+			s = gb_string_append_length(s, t->Named.name.text, t->Named.name.len);
+		}
+
+		// Write fields
+		if (pointer_depth < 2) {
+			s = gb_string_append_length(s, "=", 1);
+
+			if (!is_union) {
+				for( auto& f : base->Struct.fields ) {
+					String field_type = lb_get_objc_type_encoding(f->type, allocator, pointer_depth);
+					s = gb_string_append_length(s, field_type.text, field_type.len);
+				}
+			} else {
+				// #TODO(harold): Encode fields
+			}
+		}
+
+		s = gb_string_append_length(s, is_union ? ")" :"}", 1);
+
+		return make_string_c(s);
+	}
+
+	case Type_Generic:
+		GB_PANIC("Generic types cannot be @encoded()");
+		return str_lit("?");
+
+	case Type_Pointer: {
+		String pointee = lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1);
+		// Special case for Objective-C Objects
+		if (pointer_depth == 0 && pointee == "@") {
+			return pointee;
+		}
+
+		return concatenate_strings(allocator, str_lit("^"), pointee);
+	}
+
+	case Type_MultiPointer:
+		return concatenate_strings(allocator, str_lit("^"), lb_get_objc_type_encoding(t->Pointer.elem, allocator, pointer_depth +1));
+
+	case Type_Array: {
+		String type_str = lb_get_objc_type_encoding(t->Array.elem, allocator, pointer_depth);
+
+		gbString s = gb_string_make_reserve(allocator, type_str.len + 8);
+		s = gb_string_append_fmt(s, "[%lld%s]", t->Array.count, type_str.text);
+		return make_string_c(s);
+	}
+
+	case Type_EnumeratedArray: {
+		String type_str = lb_get_objc_type_encoding(t->EnumeratedArray.elem, allocator, pointer_depth);
+
+		gbString s = gb_string_make_reserve(allocator, type_str.len + 8);
+		s = gb_string_append_fmt(s, "[%lld%s]", t->EnumeratedArray.count, type_str.text);
+		return make_string_c(s);
+	}
+
+	case Type_Slice: {
+		String type_str = lb_get_objc_type_encoding(t->Slice.elem, allocator, pointer_depth);
+		gbString s = gb_string_make_reserve(allocator, type_str.len + 8);
+		s = gb_string_append_fmt(s, "{slice=^%s%s}", type_str, INT_SIZE_ENCODING);
+		return make_string_c(s);
+	}
+
+	case Type_DynamicArray: {
+		String type_str = lb_get_objc_type_encoding(t->DynamicArray.elem, allocator, pointer_depth);
+		gbString s = gb_string_make_reserve(allocator, type_str.len + 8);
+		s = gb_string_append_fmt(s, "{dynamic=^%s%s%sAllocator={?^v}}", type_str, INT_SIZE_ENCODING, INT_SIZE_ENCODING);
+		return make_string_c(s);
+	}
+
+	case Type_Map:
+		return str_lit("{^v^v{Allocator=?^v}}");
+	case Type_Enum:
+		return lb_get_objc_type_encoding(t->Enum.base_type, allocator, pointer_depth);
+	case Type_Tuple:
+		// NOTE(harold): Is this allowed here?
+		return str_lit("?");
+	case Type_Proc:
+		return str_lit("?");
+	case Type_BitSet:
+		return lb_get_objc_type_encoding(t->BitSet.underlying, allocator, pointer_depth);
+	case Type_SimdVector:
+		break;
+	case Type_Matrix:
+		break;
+	case Type_BitField:
+		return lb_get_objc_type_encoding(t->BitField.backing_type, allocator, pointer_depth);
+	case Type_SoaPointer: {
+		gbString s = gb_string_make_reserve(allocator, 8);
+		s = gb_string_append_fmt(s, "{=^v%s}", INT_SIZE_ENCODING);
+		return make_string_c(s);
+	}
+
+	} // End switch t->kind
+	#undef INT_SIZE_ENCODING
+
+	GB_PANIC("Unreachable");
 }
 
 struct lbObjCGlobalClass {
-    lbObjCGlobal g;
-    lbValue      class_value;    // Local registered class value
+	lbObjCGlobal g;
+	lbValue      class_value;    // Local registered class value
 };
 
 gb_internal void lb_register_objc_thing(
-    StringSet &handled,
-    lbModule *m,
-    Array<lbValue> &args,
-    Array<lbObjCGlobalClass> &class_impls,
-    StringMap<lbObjCGlobalClass> &class_map,
-    lbProcedure *p,
-    lbObjCGlobal const &g,
-    char const *call
+	StringSet &handled,
+	lbModule *m,
+	Array<lbValue> &args,
+	Array<lbObjCGlobalClass> &class_impls,
+	StringMap<lbObjCGlobalClass> &class_map,
+	lbProcedure *p,
+	lbObjCGlobal const &g,
+	char const *call
 ) {
-    if (string_set_update(&handled, g.name)) {
-        return;
-    }
-
-    lbAddr addr = {};
-    lbValue *found = string_map_get(&m->members, g.global_name);
-    if (found) {
-        addr = lb_addr(*found);
-    } else {
-        lbValue v = {};
-        LLVMTypeRef t = lb_type(m, g.type);
-        v.value = LLVMAddGlobal(m->mod, t, g.global_name);
-        v.type = alloc_type_pointer(g.type);
-        addr = lb_addr(v);
-        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{};
-
-        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;
-        }
-
-        args.count = 3;
-        args[0] = superclass == nullptr ? lb_const_nil(m, t_objc_Class) : 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);
-
-        array_add(&class_impls, lbObjCGlobalClass{g, class_ptr});
-    }
-    else {
-        args.count = 1;
-        args[0] = class_name;
-        class_ptr = lb_emit_runtime_call(p, call, args);
-    }
-
-    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;
-    }
+	if (string_set_update(&handled, g.name)) {
+		return;
+	}
+
+	lbAddr addr = {};
+	lbValue *found = string_map_get(&m->members, g.global_name);
+	if (found) {
+		addr = lb_addr(*found);
+	} else {
+		lbValue v = {};
+		LLVMTypeRef t = lb_type(m, g.type);
+		v.value = LLVMAddGlobal(m->mod, t, g.global_name);
+		v.type = alloc_type_pointer(g.type);
+		addr = lb_addr(v);
+		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{};
+
+		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;
+		}
+
+		args.count = 3;
+		args[0] = superclass == nullptr ? lb_const_nil(m, t_objc_Class) : 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);
+
+		array_add(&class_impls, lbObjCGlobalClass{g, class_ptr});
+	}
+	else {
+		args.count = 1;
+		args[0] = class_name;
+		class_ptr = lb_emit_runtime_call(p, call, args);
+	}
+
+	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;
+	}
 }
 
 gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
@@ -1513,80 +1513,80 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
 	defer (string_set_destroy(&handled));
 
 	auto args        = array_make<lbValue>(temporary_allocator(), 3, 8);
-    auto class_impls = array_make<lbObjCGlobalClass>(temporary_allocator(), 0, 16);
-
-    // Ensure classes that have been implicitly referenced through
-    // the objc_superclass attribute have a global variable available for them.
-    TypeSet class_set{};
-    type_set_init(&class_set, gen->objc_classes.count+16);
-    defer (type_set_destroy(&class_set));
-
-    auto referenced_classes = array_make<lbObjCGlobal>(temporary_allocator());
-    for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
-        array_add( &referenced_classes, g);
-
-        Type *cls = g.class_impl_type;
-        while (cls) {
-            if (type_set_update(&class_set, cls)) {
-                break;
-            }
-            GB_ASSERT(cls->kind == Type_Named);
-
-            cls = cls->Named.type_name->TypeName.objc_superclass;
-        }
-    }
-
-    for (auto pair : class_set) {
-        auto& tn = pair.type->Named.type_name->TypeName;
-        Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type;
-        lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl);
-    }
-    for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
-        array_add( &referenced_classes, g );
-    }
-
-    // Add all class globals to a map so that we can look them up dynamically
-    // in order to resolve out-of-order because classes that are being implemented
-    // need their superclasses to have been registered before them.
-    StringMap<lbObjCGlobalClass> global_class_map{};
-    string_map_init(&global_class_map, (usize)gen->objc_classes.count);
-    defer (string_map_destroy(&global_class_map));
-
-    for (lbObjCGlobal g :referenced_classes) {
-        string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g});
-    }
-
-    LLVMSetLinkage(p->value, LLVMInternalLinkage);
-    lb_begin_procedure_body(p);
-
-    // Register class globals, gathering classes that must be implemented
-    for (auto& kv : global_class_map) {
-        lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass");
-    }
-
-    // Prefetch selectors for implemented methods so that they can also be registered.
-    for (const auto& cd : class_impls) {
-        auto& g = cd.g;
-        Type *class_type = g.class_impl_type;
-
-        Array<ObjcMethodData>* methods = map_get(&m->info->objc_method_implementations, class_type);
-        if (!methods) {
-            continue;
-        }
-
-        for (const ObjcMethodData& md : *methods) {
-            lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector);
-        }
-    }
-
-    // Now we can register all referenced selectors
-    for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) {
-        lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, g, "sel_registerName");
-    }
-
-
-    // Emit method wrapper implementations and registration
-    auto wrapper_args     = array_make<Type *>(temporary_allocator(), 2, 8);
+	auto class_impls = array_make<lbObjCGlobalClass>(temporary_allocator(), 0, 16);
+
+	// Ensure classes that have been implicitly referenced through
+	// the objc_superclass attribute have a global variable available for them.
+	TypeSet class_set{};
+	type_set_init(&class_set, gen->objc_classes.count+16);
+	defer (type_set_destroy(&class_set));
+
+	auto referenced_classes = array_make<lbObjCGlobal>(temporary_allocator());
+	for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
+		array_add( &referenced_classes, g);
+
+		Type *cls = g.class_impl_type;
+		while (cls) {
+			if (type_set_update(&class_set, cls)) {
+				break;
+			}
+			GB_ASSERT(cls->kind == Type_Named);
+
+			cls = cls->Named.type_name->TypeName.objc_superclass;
+		}
+	}
+
+	for (auto pair : class_set) {
+		auto& tn = pair.type->Named.type_name->TypeName;
+		Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type;
+		lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl);
+	}
+	for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
+		array_add( &referenced_classes, g );
+	}
+
+	// Add all class globals to a map so that we can look them up dynamically
+	// in order to resolve out-of-order because classes that are being implemented
+	// need their superclasses to have been registered before them.
+	StringMap<lbObjCGlobalClass> global_class_map{};
+	string_map_init(&global_class_map, (usize)gen->objc_classes.count);
+	defer (string_map_destroy(&global_class_map));
+
+	for (lbObjCGlobal g :referenced_classes) {
+		string_map_set(&global_class_map, g.name, lbObjCGlobalClass{g});
+	}
+
+	LLVMSetLinkage(p->value, LLVMInternalLinkage);
+	lb_begin_procedure_body(p);
+
+	// Register class globals, gathering classes that must be implemented
+	for (auto& kv : global_class_map) {
+		lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass");
+	}
+
+	// Prefetch selectors for implemented methods so that they can also be registered.
+	for (const auto& cd : class_impls) {
+		auto& g = cd.g;
+		Type *class_type = g.class_impl_type;
+
+		Array<ObjcMethodData>* methods = map_get(&m->info->objc_method_implementations, class_type);
+		if (!methods) {
+			continue;
+		}
+
+		for (const ObjcMethodData& md : *methods) {
+			lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector);
+		}
+	}
+
+	// Now we can register all referenced selectors
+	for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) {
+		lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, g, "sel_registerName");
+	}
+
+
+	// Emit method wrapper implementations and registration
+	auto wrapper_args     = array_make<Type *>(temporary_allocator(), 2, 8);
 	auto get_context_args = array_make<lbValue>(temporary_allocator(), 1);
 
 
@@ -1597,186 +1597,200 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
 		map_set(&ivar_map, g.class_impl_type, g);
 	}
 
-    for (const auto& cd : class_impls) {
-        auto& g = cd.g;
-        Type *class_type = g.class_impl_type;
-    	Type *class_ptr_type = alloc_type_pointer(class_type);
-    	lbValue class_value = cd.class_value;
+	for (const auto& cd : class_impls) {
+		auto& g = cd.g;
+		Type *class_type = g.class_impl_type;
+		Type *class_ptr_type = alloc_type_pointer(class_type);
+		lbValue class_value = cd.class_value;
 
-    	Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar;
+		Type *ivar_type = class_type->Named.type_name->TypeName.objc_ivar;
 
-    	Entity *context_provider = class_type->Named.type_name->TypeName.objc_context_provider;
-    	Type *contex_provider_self_ptr_type = nullptr;
-    	Type *contex_provider_self_named_type = nullptr;
-    	bool is_context_provider_ivar = false;
-    	lbValue context_provider_proc_value{};
+		Entity *context_provider = class_type->Named.type_name->TypeName.objc_context_provider;
+		Type *contex_provider_self_ptr_type = nullptr;
+		Type *contex_provider_self_named_type = nullptr;
+		bool is_context_provider_ivar = false;
+		lbValue context_provider_proc_value{};
 
-    	if (context_provider) {
-    		context_provider_proc_value = lb_find_procedure_value_from_entity(m, context_provider);
+		if (context_provider) {
+			context_provider_proc_value = lb_find_procedure_value_from_entity(m, context_provider);
 
-    		contex_provider_self_ptr_type = base_type(context_provider->type->Proc.params->Tuple.variables[0]->type);
-    		GB_ASSERT(contex_provider_self_ptr_type->kind == Type_Pointer);
-    		contex_provider_self_named_type = base_named_type(type_deref(contex_provider_self_ptr_type));
+			contex_provider_self_ptr_type = base_type(context_provider->type->Proc.params->Tuple.variables[0]->type);
+			GB_ASSERT(contex_provider_self_ptr_type->kind == Type_Pointer);
+			contex_provider_self_named_type = base_named_type(type_deref(contex_provider_self_ptr_type));
 
-    		is_context_provider_ivar = ivar_type != nullptr && internal_check_is_assignable_to(contex_provider_self_named_type, ivar_type);
-    	}
+			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;
-        }
+		Array<ObjcMethodData>* methods = map_get(&m->info->objc_method_implementations, class_type);
+		if (!methods) {
+			continue;
+		}
 
-        for (const ObjcMethodData& md : *methods) {
-            GB_ASSERT( md.proc_entity->kind == Entity_Procedure);
-            Type *method_type = md.proc_entity->type;
+		for (const ObjcMethodData& md : *methods) {
+			GB_ASSERT( md.proc_entity->kind == Entity_Procedure);
+			Type *method_type = md.proc_entity->type;
 
-            String proc_name = make_string_c("__$objc_method::");
-            proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name);
-            proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::"));
-            proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name);
+			String proc_name = make_string_c("__$objc_method::");
+			proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name);
+			proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::"));
+			proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name);
 
-            wrapper_args.count = 2;
-            wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type;
-            wrapper_args[1] = t_objc_SEL;
+			wrapper_args.count = 2;
+			wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type;
+			wrapper_args[1] = t_objc_SEL;
 
-            auto method_param_count  = (isize)method_type->Proc.param_count;
-            i32  method_param_offset = 0;
+			auto method_param_count  = (isize)method_type->Proc.param_count;
+			i32  method_param_offset = 0;
 
-            // TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already.
-            //               (Maybe this is already accounted for?.)
-            if (!md.ac.objc_is_class_method) {
-                GB_ASSERT(method_param_count >= 1);
-                method_param_count -= 1;
-                method_param_offset = 1;
-            }
+			// TODO(harold): Need to make sure (at checker stage) that the non-class method has the self parameter already.
+			//               (Maybe this is already accounted for?.)
+			if (!md.ac.objc_is_class_method) {
+				GB_ASSERT(method_param_count >= 1);
+				method_param_count -= 1;
+				method_param_offset = 1;
+			}
 
-            for (i32 i = 0; i < method_param_count; i++) {
-                array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type);
-            }
+			for (i32 i = 0; i < method_param_count; i++) {
+				array_add(&wrapper_args, method_type->Proc.params->Tuple.variables[method_param_offset+i]->type);
+			}
 
-            Type *wrapper_args_tuple = alloc_type_tuple_from_field_types(wrapper_args.data, wrapper_args.count, false, true);
-            Type *wrapper_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, (isize)wrapper_args_tuple->Tuple.variables.count, nullptr, 0, false, ProcCC_CDecl);
+			Type *wrapper_args_tuple = alloc_type_tuple_from_field_types(wrapper_args.data, wrapper_args.count, false, true);
+			Type *wrapper_results_tuple = nullptr;
 
-            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");
+			if (method_type->Proc.result_count > 0) {
+				GB_ASSERT(method_type->Proc.result_count == 1);
+				wrapper_results_tuple = alloc_type_tuple_from_field_types(&method_type->Proc.results->Tuple.variables[0]->type, 1, false, true);
+			}
+
+			Type *wrapper_proc_type = alloc_type_proc(nullptr, wrapper_args_tuple, wrapper_args_tuple->Tuple.variables.count,
+														wrapper_results_tuple, method_type->Proc.result_count, false, ProcCC_CDecl);
 
-            // Emit the wrapper
-            LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage);
-            lb_begin_procedure_body(wrapper_proc);
-            {
-            	if (method_type->Proc.calling_convention == ProcCC_Odin) {
-            		GB_ASSERT(context_provider);
+			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");
 
-            		// Emit the get odin context call
+			// Emit the wrapper
+			LLVMSetLinkage(wrapper_proc->value, LLVMExternalLinkage);
+			lb_begin_procedure_body(wrapper_proc);
+			{
+				if (method_type->Proc.calling_convention == ProcCC_Odin) {
+					GB_ASSERT(context_provider);
 
-            		get_context_args[0] = lbValue {
-            			wrapper_proc->raw_input_parameters[0],
+					// Emit the get odin context call
+
+					get_context_args[0] = lbValue {
+						wrapper_proc->raw_input_parameters[0],
 						contex_provider_self_ptr_type,
 					};
 
-            		if (is_context_provider_ivar) {
-            			// The context provider takes the ivar's type.
-            			// Emit an obj_ivar_get call and use that pointer for 'self' instead.
-            			lbValue real_self {
-            				wrapper_proc->raw_input_parameters[0],
-            				class_ptr_type
-            			};
-            			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);
-            	}
-
-
-                auto method_call_args = array_make<lbValue>(temporary_allocator(), method_param_count + (isize)method_param_offset);
-
-                if (!md.ac.objc_is_class_method) {
-                    method_call_args[0] = lbValue {
-                        wrapper_proc->raw_input_parameters[0],
-                        class_ptr_type,
-                    };
-                }
-
-                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,
-                    };
-                }
-                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.
-                lb_emit_call(wrapper_proc, method_proc_value, method_call_args);
-            }
-            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, temporary_allocator());
-            }
-
-            if (!md.ac.objc_is_class_method) {
-                method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("@:"));
-            } else {
-                method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:"));
-            }
-
-            for (i32 i = method_param_offset; i < method_param_count; i++) {
-                Type *param_type = method_type->Proc.params->Tuple.variables[i]->type;
-                String param_encoding = lb_get_objc_type_encoding(param_type, temporary_allocator());
-
-                method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding);
-            }
-
-            // Emit method registration
-            lbAddr* sel_address = string_map_get(&m->objc_selectors, md.ac.objc_selector);
-            GB_ASSERT(sel_address);
-            lbValue selector_value = lb_addr_load(p, *sel_address);
-
-            args.count = 4;
-            args[0] = class_value;    // Class
-            args[1] = selector_value; // SEL
-            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.
-            lb_emit_runtime_call(p, "class_addMethod", args);
-
-        } // End methods
-
-        // Add ivar if we have one
-        if (ivar_type != nullptr) {
-            // Register a single ivar for this class
-            Type *ivar_base = ivar_type->Named.base;
-
-            const i64 size      = type_size_of(ivar_base);
-            const i64 alignment = type_align_of(ivar_base);
-            // TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed.
-
-        	// TODO(harold): Should we pass the actual type encoding? Might not be ideal for obfuscation.
-            String ivar_name  = str_lit("__$ivar");
-            String ivar_types = str_lit("{= }");	//lb_get_objc_type_encoding(ivar_type, temporary_allocator());// str_lit("{= }");
-            args.count = 5;
-            args[0] = class_value;
-            args[1] = lb_const_value(m, t_cstring, exact_value_string(ivar_name));
-            args[2] = lb_const_value(m, t_uint, exact_value_u64((u64)size));
-            args[3] = lb_const_value(m, t_u8, exact_value_u64((u64)alignment));
-            args[4] = lb_const_value(m, t_cstring, exact_value_string(ivar_types));
-            lb_emit_runtime_call(p, "class_addIvar", args);
-        }
-
-        // Complete the class registration
-        args.count = 1;
-        args[0] = class_value;
-        lb_emit_runtime_call(p, "objc_registerClassPair", args);
-    }
+					if (is_context_provider_ivar) {
+						// The context provider takes the ivar's type.
+						// Emit an obj_ivar_get call and use that pointer for 'self' instead.
+						lbValue real_self {
+							wrapper_proc->raw_input_parameters[0],
+							class_ptr_type
+						};
+						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);
+				}
+
+
+				auto method_call_args = array_make<lbValue>(temporary_allocator(), method_param_count + (isize)method_param_offset);
+
+				if (!md.ac.objc_is_class_method) {
+					method_call_args[0] = lbValue {
+						wrapper_proc->raw_input_parameters[0],
+						class_ptr_type,
+					};
+				}
+
+				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,
+					};
+				}
+				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);
+
+				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);
+				}
+			}
+			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, temporary_allocator());
+			}
+
+			if (!md.ac.objc_is_class_method) {
+				method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("@:"));
+			} else {
+				method_encoding = concatenate_strings(temporary_allocator(), method_encoding, str_lit("#:"));
+			}
+
+			for (i32 i = method_param_offset; i < method_param_count; i++) {
+				Type *param_type = method_type->Proc.params->Tuple.variables[i]->type;
+				String param_encoding = lb_get_objc_type_encoding(param_type, temporary_allocator());
+
+				method_encoding = concatenate_strings(temporary_allocator(), method_encoding, param_encoding);
+			}
+
+			// Emit method registration
+			lbAddr* sel_address = string_map_get(&m->objc_selectors, md.ac.objc_selector);
+			GB_ASSERT(sel_address);
+			lbValue selector_value = lb_addr_load(p, *sel_address);
+
+			args.count = 4;
+			args[0] = class_value;    // Class
+			args[1] = selector_value; // SEL
+			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.
+			lb_emit_runtime_call(p, "class_addMethod", args);
+
+		} // End methods
+
+		// Add ivar if we have one
+		if (ivar_type != nullptr) {
+			// Register a single ivar for this class
+			Type *ivar_base = ivar_type->Named.base;
+
+			const i64 size      = type_size_of(ivar_base);
+			const i64 alignment = type_align_of(ivar_base);
+			// TODO(harold): Checker: Alignment must be compatible with ivar rules. Or we should increase the alignment if needed.
+
+			// TODO(harold): Should we pass the actual type encoding? Might not be ideal for obfuscation.
+			String ivar_name  = str_lit("__$ivar");
+			String ivar_types = str_lit("{= }");	//lb_get_objc_type_encoding(ivar_type, temporary_allocator());// str_lit("{= }");
+			args.count = 5;
+			args[0] = class_value;
+			args[1] = lb_const_value(m, t_cstring, exact_value_string(ivar_name));
+			args[2] = lb_const_value(m, t_uint, exact_value_u64((u64)size));
+			args[3] = lb_const_value(m, t_u8, exact_value_u64((u64)alignment));
+			args[4] = lb_const_value(m, t_cstring, exact_value_string(ivar_types));
+			lb_emit_runtime_call(p, "class_addIvar", args);
+		}
+
+		// Complete the class registration
+		args.count = 1;
+		args[0] = class_value;
+		lb_emit_runtime_call(p, "objc_registerClassPair", args);
+	}
 
 	// Register ivar offsets for any `objc_ivar_get` expressions emitted.
 	Type *ptr_u32 = alloc_type_pointer(t_u32);

+ 3 - 3
src/llvm_backend.hpp

@@ -196,7 +196,7 @@ struct lbModule {
 
 	StringMap<lbAddr> objc_classes;
 	StringMap<lbAddr> objc_selectors;
-    StringMap<lbAddr> objc_ivars;
+	StringMap<lbAddr> objc_ivars;
 
 	PtrMap<u64/*type hash*/, lbAddr> map_cell_info_map; // address of runtime.Map_Info
 	PtrMap<u64/*type hash*/, lbAddr> map_info_map;      // address of runtime.Map_Cell_Info
@@ -220,7 +220,7 @@ struct lbObjCGlobal {
 	gbString  global_name;
 	String    name;
 	Type *    type;
-    Type *    class_impl_type;  // This is set when the class has the objc_implement attribute set to true.
+	Type *    class_impl_type;  // This is set when the class has the objc_implement attribute set to true.
 };
 
 struct lbGenerator : LinkerData {
@@ -242,7 +242,7 @@ struct lbGenerator : LinkerData {
 	MPSCQueue<lbEntityCorrection> entities_to_correct_linkage;
 	MPSCQueue<lbObjCGlobal> objc_selectors;
 	MPSCQueue<lbObjCGlobal> objc_classes;
-    MPSCQueue<lbObjCGlobal> objc_ivars;
+	MPSCQueue<lbObjCGlobal> objc_ivars;
 };
 
 

+ 2 - 2
src/llvm_backend_general.cpp

@@ -101,7 +101,7 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
 
 	string_map_init(&m->objc_classes);
 	string_map_init(&m->objc_selectors);
-    string_map_init(&m->objc_ivars);
+	string_map_init(&m->objc_ivars);
 
 	map_init(&m->map_info_map, 0);
 	map_init(&m->map_cell_info_map, 0);
@@ -174,7 +174,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
 	mpsc_init(&gen->entities_to_correct_linkage, heap_allocator());
 	mpsc_init(&gen->objc_selectors, heap_allocator());
 	mpsc_init(&gen->objc_classes, heap_allocator());
-    mpsc_init(&gen->objc_ivars, heap_allocator());
+	mpsc_init(&gen->objc_ivars, heap_allocator());
 
 	return true;
 }

+ 1 - 1
src/llvm_backend_proc.cpp

@@ -3290,7 +3290,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 	case BuiltinProc_objc_find_class:        return lb_handle_objc_find_class(p, expr);
 	case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
 	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_ivar_get:          return lb_handle_objc_ivar_get(p, expr);
 
 
 	case BuiltinProc_constant_utf16_cstring:

+ 7 - 7
src/llvm_backend_utility.cpp

@@ -2157,8 +2157,8 @@ gb_internal lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String
 
 gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_type) {
 
-    String name = self_type->Named.type_name->TypeName.objc_class_name;
-    GB_ASSERT(name != "");
+	String name = self_type->Named.type_name->TypeName.objc_class_name;
+	GB_ASSERT(name != "");
 
 	lbAddr *found = string_map_get(&m->objc_ivars, name);
 	if (found) {
@@ -2170,7 +2170,7 @@ gb_internal lbAddr lb_handle_objc_find_or_register_ivar(lbModule *m, Type *self_
 	gbString global_name = gb_string_make(permanent_allocator(), "__$objc_ivar::");
 	global_name = gb_string_append_length(global_name, name.text, name.len);
 
-    // Create a global variable to store offset of the ivar in an instance of an object
+	// Create a global variable to store offset of the ivar in an instance of an object
 	LLVMTypeRef t = lb_type(m, t_u32);
 
 	lbValue g = {};
@@ -2209,10 +2209,10 @@ gb_internal lbValue lb_handle_objc_ivar_for_objc_object_pointer(lbProcedure *p,
 }
 
 gb_internal lbValue lb_handle_objc_ivar_get(lbProcedure *p, Ast *expr) {
-    ast_node(ce, CallExpr, expr);
+	ast_node(ce, CallExpr, expr);
 
-    GB_ASSERT(ce->args[0]->tav.type->kind == Type_Pointer);
-    lbValue self = lb_build_expr(p, ce->args[0]);
+	GB_ASSERT(ce->args[0]->tav.type->kind == Type_Pointer);
+	lbValue self = lb_build_expr(p, ce->args[0]);
 
 	return lb_handle_objc_ivar_for_objc_object_pointer(p, self);
 }
@@ -2282,7 +2282,7 @@ gb_internal lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
 		GB_ASSERT(e->kind == Entity_TypeName);
 		String name = e->TypeName.objc_class_name;
 
-        Type *class_impl_type = e->TypeName.objc_is_implementation ? type : nullptr;
+		Type *class_impl_type = e->TypeName.objc_is_implementation ? type : nullptr;
 
 		return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name, class_impl_type));
 	}