diff options
| author | Harold Brenes <harold@hbrenes.com> | 2025-05-03 00:58:33 -0400 |
|---|---|---|
| committer | Harold Brenes <harold@hbrenes.com> | 2025-05-03 00:59:33 -0400 |
| commit | 5f0b47c373e34c231879b2700e78ab1bbd6219b5 (patch) | |
| tree | 4e9057eb33276486cdcaded3bb1db56ec4660ff7 /src/check_decl.cpp | |
| parent | 0746127654aaa980c3a35039c636b3ca1b794fc8 (diff) | |
Implement all checker specification for Objective-C class implementations and `objc_ivar_get` intrinsic
Diffstat (limited to 'src/check_decl.cpp')
| -rw-r--r-- | src/check_decl.cpp | 42 |
1 files changed, 33 insertions, 9 deletions
diff --git a/src/check_decl.cpp b/src/check_decl.cpp index a37d20f56..18dc5e6b0 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -565,31 +565,41 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, check_single_global_entity(ctx->checker, super->Named.type_name, super->Named.type_name->decl_info); if (super->kind != Type_Named) { + // TODO(harold): Show the current superclass token too error(e->token, "@(objc_superclass) Referenced type must be a named struct"); break; } Type* named_type = base_type(super->Named.type_name->type); if (!is_type_objc_object(named_type)) { + // TODO(harold): Show the current superclass token too error(e->token, "@(objc_superclass) Superclass must be an Objective-C class"); break; } + if (named_type->Named.type_name->TypeName.objc_class_name == "") { + // TODO(harold): Show the current superclass token too + error(e->token, "@(objc_superclass) Superclass must be have a valid @(objc_class) attribute"); + break; + } + super = named_type->Named.type_name->TypeName.objc_superclass; } } else { if (ac.objc_superclass != nullptr) { - error(e->token, "@(objc_superclass) can only be applied when the @(obj_implement) attribute is also applied"); + error(e->token, "@(objc_superclass) may only be applied when the @(obj_implement) attribute is also applied"); } else if (ac.objc_ivar != nullptr) { - error(e->token, "@(objc_ivar) can only be applied when the @(obj_implement) attribute is also applied"); + 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) can only be applied when the @(obj_implement) attribute is also applied"); + error(e->token, "@(objc_context_provider) may 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"); } + } else if (ac.objc_is_implementation) { + error(e->token, "@(objc_implement) may only be applied when the @(objc_class) attribute is also applied"); } } @@ -994,7 +1004,7 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon error(e->token, "@(objc_name) is required with @(objc_type)"); } else { Type *t = ac.objc_type; - if (t->kind == Type_Named) { + if (t->kind == Type_Named) { // TODO(harold): Shouldn't this be an error otherwise? Or is it checked elsehwere? Entity *tn = t->Named.type_name; GB_ASSERT(tn->kind == Entity_TypeName); @@ -1003,20 +1013,32 @@ 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) { + // 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_disabled_implement) { + implement = false; + } + + if (implement) { GB_ASSERT(e->kind == Entity_Procedure); - Type *proc_type = e->type; + auto &proc = e->type->Proc; + Type &first_param = proc.param_count > 0 ? proc.params[0] : *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 (proc_type->Proc.calling_convention == ProcCC_Odin && !tn->TypeName.objc_context_provider) { + } else if (!ac.objc_is_class_method && !(first_param.kind == Type_Pointer && first_param.Pointer.elem == t)) { + 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_type->Proc.calling_convention != ProcCC_CDecl) { + } 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 { - auto method = ObjcMethodData{ ac, e }; + auto method = ObjcMethodData{ ac, e }; method.ac.objc_selector = ac.objc_selector != "" ? ac.objc_selector : ac.objc_name; CheckerInfo *info = ctx->info; @@ -1033,6 +1055,8 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon map_set(&info->objc_method_implementations, t, list); } } + } else if (ac.objc_selector != "") { + error(e->token, "@(objc_selector) may only be applied to procedures that are Objective-C implementations."); } mutex_lock(&global_type_name_objc_metadata_mutex); |