diff options
| author | Username-Leon <leonardo.temperanza@gmail.com> | 2025-10-15 15:30:32 +0200 |
|---|---|---|
| committer | Username-Leon <leonardo.temperanza@gmail.com> | 2025-10-15 15:30:32 +0200 |
| commit | 4dd6bb2e871e9b8583a89e62ce36a98d02d48cc3 (patch) | |
| tree | 25b7599645caf76ebe9136da51e5114006ab93f4 /src/check_decl.cpp | |
| parent | e10093bd991334789031df9ed587d27823ca90dd (diff) | |
| parent | 596066aa0453752f24cc2fa5087fafe2c4686536 (diff) | |
Merge branch 'master' of https://github.com/LeonardoTemperanza/Odin
Diffstat (limited to 'src/check_decl.cpp')
| -rw-r--r-- | src/check_decl.cpp | 176 |
1 files changed, 129 insertions, 47 deletions
diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 842f8653c..e54943d80 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -559,6 +559,11 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *super = ac.objc_superclass; while (super != nullptr) { + if (super->kind != Type_Named) { + error(e->token, "@(objc_superclass) Referenced type must be a named struct"); + break; + } + if (type_set_update(&super_set, super)) { error(e->token, "@(objc_superclass) Superclass hierarchy cycle encountered"); break; @@ -566,11 +571,6 @@ 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) { - error(e->token, "@(objc_superclass) Referenced type must be a named struct"); - break; - } - Type* named_type = base_named_type(super); GB_ASSERT(named_type->kind == Type_Named); @@ -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"); @@ -851,6 +849,50 @@ gb_internal bool signature_parameter_similar_enough(Type *x, Type *y) { } } + Type *x_base = base_type(x); + Type *y_base = base_type(y); + + if (x_base == y_base) { + return true; + } + + if (x_base->kind == y_base->kind && + x_base->kind == Type_Struct) { + i64 xs = type_size_of(x_base); + i64 ys = type_size_of(y_base); + + i64 xa = type_align_of(x_base); + i64 ya = type_align_of(y_base); + + + if (x_base->Struct.is_raw_union == y_base->Struct.is_raw_union && + xs == ys && xa == ya) { + if (xs > 16) { + // @@ABI NOTE(bill): Just allow anything over 16-bytes to be allowed, because on all current ABIs + // it will be passed by point + // NOTE(bill): this must be changed when ABI changes + return true; + } + if (x_base->Struct.is_raw_union) { + return true; + } + if (x->Struct.fields.count == y->Struct.fields.count) { + for (isize i = 0; i < x->Struct.fields.count; i++) { + Entity *a = x->Struct.fields[i]; + Entity *b = y->Struct.fields[i]; + bool similar = signature_parameter_similar_enough(a->type, b->type); + if (!similar) { + // NOTE(bill): If the fields are not similar enough, then stop. + goto end; + } + } + } + // HACK NOTE(bill): Allow this for the time begin until it actually becomes a practical problem + return true; + } + } + +end:; return are_types_identical(x, y); } @@ -1040,61 +1082,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 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; } - } 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); defer (mutex_unlock(&global_type_name_objc_metadata_mutex)); @@ -1479,7 +1560,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 +1608,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 { |