From 4b95d6be4cb125c7389d7683522492d368703efb Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 15 Aug 2025 19:00:44 +0200 Subject: [PATCH] New method resolution. --- CMakeLists.txt | 1 + releasenotes.md | 1 + src/compiler/compiler.c | 3 +- src/compiler/compiler_internal.h | 106 ++++- src/compiler/copying.c | 28 +- src/compiler/decltable.c | 2 +- src/compiler/methodtable.c | 73 ++++ src/compiler/sema_decls.c | 361 +++++++----------- src/compiler/sema_expr.c | 116 ++---- src/compiler/sema_internal.h | 3 +- src/compiler/sema_liveness.c | 10 +- src/compiler/sema_name_resolution.c | 155 +++----- src/compiler/sema_passes.c | 2 +- src/compiler/sema_stmts.c | 6 +- src/compiler/semantic_analyser.c | 21 + src/compiler/types.c | 17 - .../methods/extending_with_visibility_fail.c3 | 8 +- .../extending_with_visibility_fail_private.c3 | 4 +- test/test_suite/methods/operator_mismatch.c3 | 5 +- 19 files changed, 440 insertions(+), 482 deletions(-) create mode 100644 src/compiler/methodtable.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 7913ca01d..6c7741e87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -390,6 +390,7 @@ add_executable(c3c src/utils/unzipper.c src/compiler/c_codegen.c src/compiler/decltable.c + src/compiler/methodtable.c src/compiler/mac_support.c src/compiler/windows_support.c src/compiler/codegen_asm.c diff --git a/releasenotes.md b/releasenotes.md index 089f10200..4c21a76e7 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -11,6 +11,7 @@ - Deprecate `@compact` use for comparison. Old behaviour is enabled using `--use-old-compact-eq`. - Switch available for types implementing `@operator(==)`. - `Type.is_eq` is now true for types with `==` overload. +- Methods ignore visibility settings. ### Fixes - List.remove_at would incorrectly trigger ASAN. diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 25f680c6d..87fcb4be3 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -75,9 +75,10 @@ void compiler_init(BuildOptions *build_options) htable_init(&compiler.context.features, 1024); htable_init(&compiler.context.compiler_defines, 16 * 1024); + methodtable_init(&compiler.context.method_extensions, 16 * 1024); compiler.context.module_list = NULL; compiler.context.generic_module_list = NULL; - compiler.context.method_extensions = NULL; + compiler.context.method_extension_list = NULL; vmem_init(&ast_arena, START_VMEM_SIZE); ast_calloc(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index c47a1de61..663ba6613 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -121,6 +121,22 @@ typedef struct TypeKind type; } Float; +typedef struct +{ + uint32_t count; + uint32_t capacity; + uint32_t max_load; + DeclId *methods; +} MethodTable; + +typedef struct +{ + uint32_t count; + uint32_t capacity; + uint32_t max_load; + DeclId *entries; +} DeclTable; + struct ConstInitializer_ { ConstInitType kind; @@ -619,6 +635,13 @@ typedef struct AstId parent; } LabelDecl; +typedef struct +{ + DeclId overloads[OVERLOADS_COUNT + 1]; + DeclTable method_table; + Decl **methods; +} Methods; + typedef struct Decl_ { const char *name; @@ -678,7 +701,7 @@ typedef struct Decl_ struct { TypeInfo **interfaces; - Decl **methods; + Methods *method_table; union { // Enums and Fault @@ -1570,7 +1593,6 @@ typedef struct Module_ AnalysisStage stage : 6; AstId contracts; - Decl** private_method_extensions; HTable symbols; struct CompilationUnit_ **units; Module *generic_module; @@ -1652,14 +1674,6 @@ typedef struct LexMode mode; } Lexer; -typedef struct -{ - uint32_t count; - uint32_t capacity; - uint32_t max_load; - DeclId *entries; -} DeclTable; - struct CompilationUnit_ { Module *module; @@ -1697,7 +1711,6 @@ struct CompilationUnit_ Decl *main_function; HTable local_symbols; int lambda_count; - Decl **local_method_extensions; TypeInfo **check_type_variable_array; struct { @@ -1899,7 +1912,6 @@ typedef struct Module **module_list; Module **generic_module_list; Type **type; - Decl **method_extensions; const char *lib_dir; const char **sources; File **loaded_sources; @@ -1911,6 +1923,8 @@ typedef struct HTable compiler_defines; HTable features; Module std_module; + MethodTable method_extensions; + Decl **method_extension_list; DeclTable symbols; PathTable path_symbols; DeclTable generic_symbols; @@ -2160,6 +2174,7 @@ UNUSED bool i128_get_bit(const Int128 *op, int bit); #define MACRO_COPY_DECL(x) x = copy_decl(c, x) #define MACRO_COPY_DECLID(x) x = declid_copy_deep(c, x) #define MACRO_COPY_DECL_LIST(x) x = copy_decl_list(c, x) +#define MACRO_COPY_DECL_METHODS(x) x = copy_decl_methods(c, x) #define MACRO_COPY_EXPR(x) x = copy_expr(c, x) #define MACRO_COPY_EXPRID(x) x = exprid_copy_deep(c, x) #define MACRO_COPY_TYPE(x) x = copy_type_info(c, x) @@ -2370,6 +2385,7 @@ Path *path_create_from_string(const char *string, uint32_t len, SourceSpan span) typedef enum FindMember { METHODS_AND_FIELDS, + METHODS_INTERFACES_AND_FIELDS, FIELDS_ONLY } FindMember; @@ -2418,8 +2434,9 @@ void sema_expr_convert_enum_to_int(Expr *expr); Decl *sema_decl_stack_resolve_symbol(const char *symbol); Decl *sema_find_decl_in_modules(Module **module_list, Path *path, const char *interned_name); bool unit_resolve_parameterized_symbol(SemaContext *context, NameResolve *name_resolve); -Decl *sema_resolve_type_method(CompilationUnit *unit, Type *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref); -Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref); +Decl *sema_resolve_type_method(CanonicalType *type, const char *method_name); +Decl *sema_resolve_method(Decl *type, const char *method_name); +Decl *sema_resolve_method_only(Decl *type, const char *method_name); Decl *sema_find_extension_method_in_list(Decl **extensions, Type *type, const char *method_name); bool sema_resolve_type_decl(SemaContext *context, Type *type); bool sema_check_type_variable_array(SemaContext *context, TypeInfo *type); @@ -2476,6 +2493,10 @@ void decltable_init(DeclTable *table, uint32_t initial_size); DeclId decltable_get(DeclTable *table, const char *name); void decltable_set(DeclTable *table, Decl *decl); +void methodtable_init(MethodTable *table, uint32_t initial_size); +DeclId methodtable_get(MethodTable *table, Type *type, const char *name); +DeclId methodtable_set(MethodTable *table, Decl *method); + const char *scratch_buffer_interned(void); const char *scratch_buffer_interned_as(TokenType *type); @@ -2542,7 +2563,6 @@ bool type_is_abi_aggregate(Type *type); bool type_is_int128(Type *type); Type *type_from_token(TokenType type); -bool type_is_user_defined(Type *type); bool type_is_structurally_equivalent(Type *type1, Type *type); bool type_flat_is_floatlike(Type *type); bool type_flat_is_intlike(Type *type); @@ -2608,6 +2628,7 @@ INLINE TypeInfo *type_info_new(TypeInfoKind kind, SourceSpan span); INLINE TypeInfo *type_info_new_base(Type *type, SourceSpan span); INLINE bool type_info_ok(TypeInfo *type_info); INLINE bool type_info_poison(TypeInfo *type); +INLINE bool type_is_user_defined(Type *type); int type_kind_bitsize(TypeKind kind); INLINE bool type_kind_is_signed(TypeKind kind); @@ -3019,6 +3040,46 @@ INLINE Type *type_flatten_for_bitstruct(Type *type) return type; } +static inline void methods_add(Methods *methods, Decl *method) +{ + vec_add(methods->methods, method); + OperatorOverload operator = method->func_decl.operator; + if (operator) + { + unsigned len = vec_size(method->func_decl.signature.params); + if (operator == OVERLOAD_MINUS && len == 1) + { + method->func_decl.operator = operator = OVERLOAD_UNARY_MINUS; + } + + if (len > 1 && !method->func_decl.signature.params[1]->var.type_info) + { + method->func_decl.is_wildcard_overload = true; + } + DeclId *decl = &methods->overloads[operator]; + if (!*decl) + { + *decl = declid(method); + } + else + { + Decl *current = declptr(*decl); + if (current->decl_kind != DECL_DECLARRAY) + { + Decl *decl_array = decl_new(DECL_DECLARRAY, NULL, INVALID_SPAN); + vec_add(decl_array->decls, declptr(*decl)); + vec_add(decl_array->decls, method); + *decl = declid(decl_array); + } + else + { + vec_add(current->decls, method); + } + } + } + decltable_set(&methods->method_table, method); +} + static inline Type *type_base(Type *type) { while (1) @@ -3147,6 +3208,21 @@ static inline Type *type_flat_distinct_enum_inline(Type *type) } } +INLINE bool type_is_user_defined(Type *type) +{ + static const bool user_defined_types[TYPE_LAST + 1] = { + [TYPE_ENUM] = true, + [TYPE_STRUCT] = true, + [TYPE_FUNC_RAW] = true, + [TYPE_UNION] = true, + [TYPE_DISTINCT] = true, + [TYPE_BITSTRUCT] = true, + [TYPE_TYPEDEF] = true, + [TYPE_INTERFACE] = true, + }; + return user_defined_types[type->type_kind]; +} + static inline Type *type_flatten_to_int(Type *type) { while (1) diff --git a/src/compiler/copying.c b/src/compiler/copying.c index c41b3a3e5..d2a1472a3 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -16,6 +16,7 @@ static Ast *ast_copy_deep(CopyStruct *c, Ast *source); static Ast **copy_ast_list(CopyStruct *c, Ast **to_copy); static Decl *copy_decl(CopyStruct *c, Decl *decl); static Decl **copy_decl_list(CopyStruct *c, Decl **decl_list); +static Methods *copy_decl_methods(CopyStruct *c, Methods *methods); static TypeInfo *copy_type_info(CopyStruct *c, TypeInfo *source); static inline void copy_reg_ref(CopyStruct *c, void *original, void *result) @@ -871,8 +872,6 @@ static ResolvedAttrData *copy_attrs_resolved(CopyStruct *c, ResolvedAttrData *da .section = data->section, .wasm_module = data->wasm_module }; - const char **new = NULL; - return copy; } @@ -913,6 +912,19 @@ Decl **copy_decl_list(CopyStruct *c, Decl **decl_list) return result; } +static Methods *copy_decl_methods(CopyStruct *c, Methods *methods) +{ + if (!methods) return NULL; + Methods *copy = CALLOCS(Methods); + decltable_init(©->method_table, 64); + + FOREACH(Decl *, method, methods->methods) + { + methods_add(copy, copy_decl(c, method)); + } + return copy; +} + TypeInfo *copy_type_info(CopyStruct *c, TypeInfo *source) { if (!source) return NULL; @@ -1004,7 +1016,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) case DECL_INTERFACE: copy_decl_type(copy); MACRO_COPY_TYPE_LIST(copy->interfaces); - MACRO_COPY_DECL_LIST(copy->methods); + MACRO_COPY_DECL_METHODS(copy->method_table); MACRO_COPY_DECL_LIST(copy->interface_methods); break; case DECL_CT_EXEC: @@ -1024,7 +1036,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) MACRO_COPY_TYPE_LIST(copy->interfaces); MACRO_COPY_DECL_LIST(copy->strukt.members); MACRO_COPY_DECLID(copy->strukt.padded_decl_id); - MACRO_COPY_DECL_LIST(copy->methods); + MACRO_COPY_DECL_METHODS(copy->method_table); break; case DECL_DECLARRAY: case DECL_GROUP: @@ -1035,21 +1047,21 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) MACRO_COPY_TYPE_LIST(copy->interfaces); MACRO_COPY_DECL_LIST(copy->strukt.members); MACRO_COPY_TYPE(copy->strukt.container_type); - MACRO_COPY_DECL_LIST(copy->methods); + MACRO_COPY_DECL_METHODS(copy->method_table); break; case DECL_FAULT: break; case DECL_CONST_ENUM: copy_decl_type(copy); MACRO_COPY_TYPE_LIST(copy->interfaces); - MACRO_COPY_DECL_LIST(copy->methods); + MACRO_COPY_DECL_METHODS(copy->method_table); MACRO_COPY_TYPE(copy->enums.type_info); MACRO_COPY_DECL_LIST(copy->enums.values); break; case DECL_ENUM: copy_decl_type(copy); MACRO_COPY_TYPE_LIST(copy->interfaces); - MACRO_COPY_DECL_LIST(copy->methods); + MACRO_COPY_DECL_METHODS(copy->method_table); MACRO_COPY_DECL_LIST(copy->enums.parameters); MACRO_COPY_TYPE(copy->enums.type_info); MACRO_COPY_DECL_LIST(copy->enums.values); @@ -1112,7 +1124,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) case DECL_DISTINCT: copy_decl_type(copy); MACRO_COPY_TYPE_LIST(copy->interfaces); - MACRO_COPY_DECL_LIST(copy->methods); + MACRO_COPY_DECL_METHODS(copy->method_table); MACRO_COPY_TYPE(copy->distinct); break; case DECL_CT_ECHO: diff --git a/src/compiler/decltable.c b/src/compiler/decltable.c index 917aa8aa8..24405f3de 100644 --- a/src/compiler/decltable.c +++ b/src/compiler/decltable.c @@ -89,4 +89,4 @@ void decltable_init(DeclTable *table, uint32_t initial_size) table->capacity = initial_size; table->max_load = (uint32_t)(initial_size * TABLE_MAX_LOAD); table->entries = entries; -} \ No newline at end of file +} diff --git a/src/compiler/methodtable.c b/src/compiler/methodtable.c new file mode 100644 index 000000000..f677e98f0 --- /dev/null +++ b/src/compiler/methodtable.c @@ -0,0 +1,73 @@ +#include "compiler_internal.h" + +static inline DeclId *methodentry_find(DeclId *entries, uint32_t capacity, const char *name, Type *type) +{ + uintptr_t hash_key = (uintptr_t)name ^ (uintptr_t)type; + uint32_t mask = capacity - 1; + hash_key ^= hash_key >> 16; + uint32_t index = (uint32_t)hash_key & mask; + while (1) + { + DeclId *entry = &entries[index]; + DeclId decl_id = *entry; + if (!decl_id) return entry; + Decl *decl = declptr(*entry); + if (decl->name == name && typeget(decl->func_decl.type_parent) == type) return entry; + index = (index + 1) & mask; + } +} + +static inline void methodtable_resize(MethodTable *table) +{ + ASSERT(table->capacity < MAX_HASH_SIZE && "Table size too large, exceeded max hash size"); + uint32_t new_capacity = table->capacity ? (table->capacity << 2u) : 16u; + DeclId *new_data = CALLOC(new_capacity * sizeof(DeclId)); + table->count = 0; + uint32_t len = table->capacity; + for (uint32_t i = 0; i < len; i++) + { + DeclId *entry = &table->methods[i]; + DeclId id = *entry; + if (!id) continue; + Decl *decl = declptr(id); + table->count++; + DeclId *dest = methodentry_find(new_data, new_capacity, decl->name, typeget(decl->func_decl.type_parent)); + *dest = id; + } + table->methods = new_data; + table->max_load = (uint32_t)(new_capacity * TABLE_MAX_LOAD); + table->capacity = new_capacity; +} + +DeclId methodtable_set(MethodTable *table, Decl *decl) +{ + ASSERT(decl && "Cannot insert NULL"); + DeclId *entry = methodentry_find(table->methods, table->capacity, decl->name, typeget(decl->func_decl.type_parent)); + DeclId decl_id = declid(decl); + DeclId old_id = *entry; + if (old_id) return old_id; + *entry = decl_id; + table->count++; + if (table->count >= table->max_load) methodtable_resize(table); + return 0; +} + + +DeclId methodtable_get(MethodTable *table, Type *type, const char *name) +{ + if (!table->methods) return 0; + DeclId *entry = methodentry_find(table->methods, table->capacity, name, type); + return *entry; +} + +void methodtable_init(MethodTable *table, uint32_t initial_size) +{ + ASSERT(initial_size && "Size must be larger than 0"); + assert (is_power_of_two(initial_size) && "Must be a power of two"); + + DeclId *entries = CALLOC(initial_size * sizeof(DeclId)); + table->count = 0; + table->capacity = initial_size; + table->max_load = (uint32_t)(initial_size * TABLE_MAX_LOAD); + table->methods = entries; +} diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 00fbd29cb..30a7466df 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -9,18 +9,13 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *er static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfo *method_parent, Decl *decl); static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl); static inline bool sema_check_param_uniqueness_and_type(SemaContext *context, Decl **decls, Decl *current, - unsigned current_index, unsigned count); + unsigned current_index); static inline bool sema_analyse_method(SemaContext *context, Decl *decl); static inline bool sema_is_valid_method_param(SemaContext *context, Decl *param, Type *parent_type, bool is_dynamic); static inline bool sema_analyse_macro_method(SemaContext *context, Decl *decl); -static inline bool unit_add_base_extension_method(SemaContext *context, CompilationUnit *unit, Type *parent_type, - Decl *method); -static inline bool unit_add_method(SemaContext *context, Type *parent_type, Decl *method); -static bool sema_analyse_operator_common(SemaContext *context, Decl *method, TypeInfo **rtype_ptr, Decl ***params_ptr, - uint32_t parameters); -static inline OverloadMatch operator_in_module_typed(SemaContext *c, Module *module, OperatorOverload operator_overload, - OverloadType overload_type, Type *method_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, OverloadMatch last_match, Decl **ambiguous_ref); -static inline Decl *operator_in_module_exact_typed(Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Type *param_type, Decl *skipped); +static inline bool unit_add_base_extension_method(SemaContext *context, Decl *method); +static inline bool type_add_method(SemaContext *context, Type *parent_type, Decl *method); +static bool sema_analyse_operator_common(SemaContext *context, Decl *method, TypeInfo **rtype_ptr, Decl ***params_ptr, uint32_t parameters); static inline bool sema_analyse_operator_element_at(SemaContext *context, Decl *method); static inline bool sema_analyse_operator_element_set(SemaContext *context, Decl *method); static inline bool sema_analyse_operator_len(SemaContext *context, Decl *method); @@ -40,13 +35,11 @@ static inline bool sema_analyse_doc_header(SemaContext *context, AstId doc, Decl static const char *attribute_domain_to_string(AttributeDomain domain); static bool sema_analyse_attribute(SemaContext *context, ResolvedAttrData *attr_data, Decl *decl, Attr *attr, AttributeDomain domain, bool *erase_decl); -static bool sema_analyse_attributes_inner(SemaContext *context, ResolvedAttrData *attr_data, Decl *decl, Attr **attrs, AttributeDomain domain, +static bool sema_analyse_attributes_inner(SemaContext *context, ResolvedAttrData *attr_data_ref, Decl *decl, Attr **attrs, AttributeDomain domain, Decl *top, bool *erase_decl); static bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl, bool *erase_decl); static bool sema_check_section(SemaContext *context, Attr *attr); static inline bool sema_analyse_attribute_decl(SemaContext *context, SemaContext *c, Decl *decl, bool *erase_decl); -static OverloadMatch sema_find_typed_operator_in_list(SemaContext *context, Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, OverloadMatch current_match, Decl **ambiguous_ref); -static Decl *sema_find_exact_typed_operator_in_list(Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Type *binary_type, Decl *skipped); static inline bool sema_analyse_typedef(SemaContext *context, Decl *decl, bool *erase_decl); static bool sema_analyse_variable_type(SemaContext *context, Type *type, SourceSpan span); @@ -97,7 +90,7 @@ static bool sema_check_section(SemaContext *context, Attr *attr) * Check parameter name uniqueness and that the type is not void. */ static inline bool sema_check_param_uniqueness_and_type(SemaContext *context, Decl **decls, Decl *current, - unsigned current_index, unsigned count) + unsigned current_index) { const char *name = current->name; @@ -1420,7 +1413,7 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, if (!sema_analyse_expr_rhs(context, param->type, expr, true, NULL, false)) return decl_poison(param); } } - if (!sema_check_param_uniqueness_and_type(context, params, param, i, param_count)) return decl_poison(param); + if (!sema_check_param_uniqueness_and_type(context, params, param, i)) return decl_poison(param); param->resolve_status = RESOLVE_DONE; } return true; @@ -1855,122 +1848,64 @@ static bool sema_analyse_operator_common(SemaContext *context, Decl *method, Typ return true; } -INLINE bool decl_matches_overload(Decl *method, Type *type, OperatorOverload overload) -{ - return method->func_decl.operator == overload && typeget(method->func_decl.type_parent)->canonical == type; -} -static inline OverloadMatch operator_in_module_typed(SemaContext *c, Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, OverloadMatch match, Decl **ambiguous_ref) -{ - if (match == OVERLOAD_MATCH_ERROR) return match; - if (module->is_generic) return match; - match = sema_find_typed_operator_in_list(c, module->private_method_extensions, operator_overload, OVERLOAD_TYPE_SYMMETRIC, method_type, binary_arg, binary_type, candidate_ref, match, ambiguous_ref); - FOREACH(Module *, sub_module, module->sub_modules) - { - match = operator_in_module_typed(c, sub_module, operator_overload, overload_type, method_type, binary_arg, binary_type, candidate_ref, match, ambiguous_ref); - } - return match; -} - -static inline Decl *operator_in_module_exact_typed(Module *module, OperatorOverload operator_overload, OverloadType overload_type, Type *method_type, Type *param_type, Decl *skipped) -{ - if (module->is_generic) return NULL; - Decl *found = sema_find_exact_typed_operator_in_list(module->private_method_extensions, operator_overload, overload_type, method_type, param_type, skipped); - if (found) return found; - FOREACH(Module *, sub_module, module->sub_modules) - { - return operator_in_module_exact_typed(sub_module, operator_overload, overload_type, method_type, param_type, skipped); - } - return NULL; -} - -static inline Decl *operator_in_module_untyped(Module *module, Type *type, OperatorOverload operator_overload, Decl *skipped) -{ - if (module->is_generic) return NULL; - FOREACH(Decl *, extension, module->private_method_extensions) - { - if (extension == skipped) continue; - if (decl_matches_overload(extension, type, operator_overload)) - { - return extension; - } - } - FOREACH(Module *, sub_module, module->sub_modules) - { - return operator_in_module_untyped(sub_module, type, operator_overload, skipped); - } - return NULL; -} - -Decl *sema_find_untyped_operator(SemaContext *context, Type *type, OperatorOverload operator_overload, Decl *skipped) +Decl *sema_find_untyped_operator(Type *type, OperatorOverload operator_overload, Decl *skipped) { type = type->canonical; assert(operator_overload < OVERLOAD_TYPED_START); if (!type_may_have_sub_elements(type)) return NULL; Decl *def = type->decl; - FOREACH(Decl *, func, def->methods) + if (!def->method_table) return NULL; + Decl *decl = declptrzero(def->method_table->overloads[operator_overload]); + if (!decl) return NULL; + if (decl->decl_kind != DECL_DECLARRAY) { - if (skipped == func) continue; - if (func->func_decl.operator == operator_overload) return func; + return decl == skipped ? NULL : decl; } - FOREACH(Decl *, extension, context->unit->local_method_extensions) + FOREACH(Decl *, candidate, decl->decls) { - if (skipped == extension) continue; - if (decl_matches_overload(extension, type, operator_overload)) return extension; - } - Decl *extension = operator_in_module_untyped(context->compilation_unit->module, type, operator_overload, skipped); - if (extension) return extension; - FOREACH(Decl *, import, context->unit->public_imports) - { - extension = operator_in_module_untyped(import->import.module, type, operator_overload, skipped); - if (extension) return extension; + return candidate == skipped ? NULL : candidate; } return NULL; } -static Decl *sema_find_exact_typed_operator_in_list(Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Type *binary_type, Decl *skipped) -{ - Decl *wildcard = NULL; - FOREACH(Decl *, func, methods) - { - if (func == skipped) continue; - if (!decl_ok(func)) continue; - if (func->func_decl.operator != operator_overload) continue; - if (parent_type && parent_type != typeget(func->func_decl.type_parent)) continue; - if ((overload_type & func->func_decl.overload_type) == 0) continue; - if (func->func_decl.is_wildcard_overload) - { - wildcard = func; - continue; - } - Type *first_arg = func->func_decl.signature.params[1]->type; - if (first_arg->canonical != binary_type) continue; - return func; - } - return wildcard; -} -static OverloadMatch sema_find_typed_operator_in_list(SemaContext *context, Decl **methods, OperatorOverload operator_overload, OverloadType overload_type, Type *parent_type, Expr *binary_arg, Type *binary_type, Decl **candidate_ref, OverloadMatch last_match, Decl **ambiguous_ref) + +OverloadMatch sema_find_typed_operator_type(SemaContext *context, OperatorOverload operator_overload, OverloadType overload_type, Type *lhs_type, Type *rhs_type, Expr *rhs, Decl **candidate_ref, OverloadMatch last_match, Decl **ambiguous_ref) { - if (last_match == OVERLOAD_MATCH_AMBIGUOUS_EXACT || last_match == OVERLOAD_MATCH_ERROR) return last_match; + Methods *methods = lhs_type->decl->method_table; + if (!methods || last_match == OVERLOAD_MATCH_AMBIGUOUS_EXACT || last_match == OVERLOAD_MATCH_ERROR) return last_match; + Decl *decl = declptrzero(methods->overloads[operator_overload]); + if (!decl) return last_match; + Decl **decls; + unsigned count; Decl *candidate = *candidate_ref; - FOREACH(Decl *, func, methods) + if (decl->decl_kind == DECL_DECLARRAY) { + decls = decl->decls; + count = vec_size(decls); + } + else + { + decls = &decl; + count = 1; + } + for (unsigned i = 0; i < count; i++) + { + Decl *func = decls[i]; if (!sema_analyse_decl(context, func)) return OVERLOAD_MATCH_ERROR; ASSERT_SPAN(func, func->resolve_status == RESOLVE_DONE); - if (func->func_decl.operator != operator_overload) continue; - if (parent_type && parent_type != typeget(func->func_decl.type_parent)) continue; - if ((overload_type & func->func_decl.overload_type) == 0) continue; if (candidate == func) continue; + if ((overload_type & func->func_decl.overload_type) == 0) continue; OverloadMatch match = OVERLOAD_MATCH_WILDCARD; if (!func->func_decl.is_wildcard_overload) { Type *first_arg = func->func_decl.signature.params[1]->type->canonical; match = OVERLOAD_MATCH_EXACT; - if (first_arg != binary_type) + if (first_arg != rhs_type) { - if (!binary_arg) continue; - if (!may_cast(context, binary_arg, first_arg, false, true)) continue; + if (!rhs) continue; + if (!may_cast(context, rhs, first_arg, false, true)) continue; match = OVERLOAD_MATCH_CONVERSION; } } @@ -2010,7 +1945,7 @@ static OverloadMatch sema_find_typed_operator_in_list(SemaContext *context, Decl } UNREACHABLE; } -MATCH: + MATCH: candidate = func; last_match = match; } @@ -2018,54 +1953,6 @@ MATCH: return last_match; } -static Decl *sema_find_exact_typed_operator(SemaContext *context, Type *type, OperatorOverload operator_overload, OverloadType overload_type, Type *param_type, Decl *skipped) -{ - assert(operator_overload >= OVERLOAD_TYPED_START); - type = type->canonical; - - Decl *func = sema_find_exact_typed_operator_in_list(type->decl->methods, operator_overload, overload_type, type, - param_type, skipped); - if (func) return func; - - Decl *extension = sema_find_exact_typed_operator_in_list(context->unit->local_method_extensions, - operator_overload, overload_type, type, param_type, skipped); - if (extension) return extension; - - extension = operator_in_module_exact_typed(context->compilation_unit->module, operator_overload, overload_type, - type, param_type, skipped); - if (extension) return extension; - - FOREACH(Decl *, import, context->unit->imports) - { - if (!import->import.import_private_as_public) continue; - extension = operator_in_module_exact_typed(import->import.module, operator_overload, overload_type, - type, param_type, skipped); - if (extension) return extension; - } - return NULL; -} - -OverloadMatch sema_find_typed_operator_type(SemaContext *context, OperatorOverload operator_overload, OverloadType overloat_type, Type *lhs_type, Type *rhs_type, Expr *rhs, Decl **candidate_ref, OverloadMatch last_match, Decl **ambiguous_ref) -{ - // Can we find the overload directly on the method? - last_match = sema_find_typed_operator_in_list( - context, lhs_type->decl->methods, - operator_overload, overloat_type, lhs_type, - rhs, rhs_type, candidate_ref, last_match, ambiguous_ref); - last_match = sema_find_typed_operator_in_list(context, context->unit->local_method_extensions, - operator_overload, overloat_type, lhs_type, rhs, rhs_type, candidate_ref, last_match, ambiguous_ref); - // Can we find it in the current module? - last_match = operator_in_module_typed(context, context->compilation_unit->module, operator_overload, overloat_type, - lhs_type, rhs, rhs_type, candidate_ref, last_match, ambiguous_ref); - - FOREACH(Decl *, import, context->unit->public_imports) - { - last_match = operator_in_module_typed(context, import->import.module, operator_overload, overloat_type, - lhs_type, rhs, rhs_type, candidate_ref, last_match, ambiguous_ref); - } - return last_match; -} - const char *operator_overload_to_string(OperatorOverload operator_overload) { switch (operator_overload) @@ -2157,10 +2044,6 @@ static inline bool sema_analyse_operator_unary(SemaContext *context, Decl *metho static inline bool sema_analyse_operator_arithmetics(SemaContext *context, Decl *method, OperatorOverload operator_overload) { - if (operator_overload == OVERLOAD_MINUS && vec_size(method->func_decl.signature.params) < 2) - { - return sema_analyse_operator_unary(context, method, method->func_decl.operator = OVERLOAD_UNARY_MINUS); - } Signature *signature = &method->func_decl.signature; Decl **params = signature->params; uint32_t param_count = vec_size(params); @@ -2278,9 +2161,8 @@ static bool sema_check_operator_method_validity(SemaContext *context, Decl *meth case OVERLOAD_SHL_ASSIGN: return sema_analyse_operator_arithmetics(context, method, operator); case OVERLOAD_NEGATE: - return sema_analyse_operator_unary(context, method, operator); case OVERLOAD_UNARY_MINUS: - // Changed in OVERLOAD_MINUS analysis + return sema_analyse_operator_unary(context, method, operator); UNREACHABLE } ASSERT_SPANF(method, false, "Method had unexpected operator %d", operator); @@ -2317,22 +2199,17 @@ INLINE SourceSpan method_find_overload_span(Decl *method) return method->attrs_resolved->overload; } -static inline bool unit_add_base_extension_method(UNUSED SemaContext *context, CompilationUnit *unit, Type *parent_type, Decl *method) +static inline bool unit_add_base_extension_method(SemaContext *context, Decl *method) { - // Add it to the right list of extensions. - switch (method->visibility) + Decl *other = declptrzero(methodtable_set(&compiler.context.method_extensions, method)); + if (other) { - case VISIBLE_PUBLIC: - vec_add(compiler.context.method_extensions, method); - break; - case VISIBLE_PRIVATE: - vec_add(unit->module->private_method_extensions, method); - break; - case VISIBLE_LOCAL: - vec_add(unit->local_method_extensions, method); - break; + SEMA_ERROR(method, "This %s is already defined.", method_name_by_decl(method)); + SEMA_NOTE(other, "The previous definition was here."); + return false; } - DEBUG_LOG("Method-like '%s.%s' analysed.", parent_type->name, method->name); + vec_add(compiler.context.method_extension_list, method); + DEBUG_LOG("Builtin type method '%s' analysed.", method->name); return true; } @@ -2421,8 +2298,8 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type // See if the operator has already been defined. OperatorOverload operator = method->func_decl.operator; - Type *second_param = NULL; + bool is_wildcard = false; if (vec_size(method->func_decl.signature.params) > 1) { second_param = method->func_decl.signature.params[1]->type; @@ -2432,7 +2309,7 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type { RETURN_SEMA_ERROR(method, "Only regular overloads can have untyped right hand parameters"); } - method->func_decl.is_wildcard_overload = true; + is_wildcard = method->func_decl.is_wildcard_overload = true; second_param = type_void; } second_param = second_param->canonical; @@ -2446,11 +2323,46 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type Decl *other = NULL; if (operator >= OVERLOAD_TYPED_START) { - other = sema_find_exact_typed_operator(context, parent_type, operator, method->func_decl.overload_type, second_param, method); // NOLINT + Methods *methods = parent_type->decl->method_table; + if (!methods) goto NONE; + Decl *decl = declptrzero(methods->overloads[operator]); + if (!decl) goto NONE; + Decl **decls; + unsigned count; + if (decl->decl_kind == DECL_DECLARRAY) + { + decls = decl->decls; + count = vec_size(decls); + } + else + { + decls = &decl; + count = 1; + } + OverloadType type = method->func_decl.overload_type; + assert(type); + for (unsigned i = 0; i < count; i++) + { + Decl *func = decls[i]; + if (func == method) continue; + assert(func->func_decl.operator); + if ((func->func_decl.overload_type & type) == 0) continue; + if (!sema_analyse_decl(context, func)) return false; + ASSERT_SPAN(func, func->decl_kind == DECL_FUNC || func->decl_kind == DECL_MACRO); + if (is_wildcard && func->func_decl.is_wildcard_overload) + { + other = func; + break; + } + if (is_wildcard || func->func_decl.is_wildcard_overload) continue; + if (func->func_decl.signature.params[1]->type->canonical != second_param) continue; + other = func; + break; + } } else { - other = sema_find_untyped_operator(context, parent_type, operator, method); + other = sema_find_untyped_operator(parent_type, operator, method); } if (other) { @@ -2459,7 +2371,7 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type SEMA_NOTE(other, "The previous definition was here."); return false; } - +NONE: if (parent_type->canonical->type_kind == TYPE_BITSTRUCT) { switch (operator) @@ -2490,48 +2402,54 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type return true; case OVERLOAD_ELEMENT_AT: // [] compares &[] - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_REF, method); - if (other && decl_ok(other)) + other = sema_find_untyped_operator(parent_type, OVERLOAD_ELEMENT_REF, method); + if (other) { + if (!sema_analyse_decl(context, other)) return false; sema_get_overload_arguments(other, &value, &index_type); break; } // And []= - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_SET, method); - if (other && decl_ok(other)) + other = sema_find_untyped_operator(parent_type, OVERLOAD_ELEMENT_SET, method); + if (other) { + if (!sema_analyse_decl(context, other)) return false; sema_get_overload_arguments(other, &value, &index_type); break; } return true; case OVERLOAD_ELEMENT_REF: // &[] compares [] - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_AT, method); - if (other && decl_ok(other)) + other = sema_find_untyped_operator(parent_type, OVERLOAD_ELEMENT_AT, method); + if (other) { + if (!sema_analyse_decl(context, other)) return false; sema_get_overload_arguments(other, &value, &index_type); break; } // And []= - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_SET, method); - if (other && decl_ok(other)) + other = sema_find_untyped_operator(parent_type, OVERLOAD_ELEMENT_SET, method); + if (other) { + if (!sema_analyse_decl(context, other)) return false; sema_get_overload_arguments(other, &value, &index_type); break; } return true; case OVERLOAD_ELEMENT_SET: // []= compares &[] - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_REF, method); - if (other && decl_ok(other)) + other = sema_find_untyped_operator(parent_type, OVERLOAD_ELEMENT_REF, method); + if (other) { + if (!sema_analyse_decl(context, other)) return false; sema_get_overload_arguments(other, &value, &index_type); break; } // And [] - other = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_AT, method); - if (other && decl_ok(other)) + other = sema_find_untyped_operator(parent_type, OVERLOAD_ELEMENT_AT, method); + if (other) { + if (!sema_analyse_decl(context, other)) return false; sema_get_overload_arguments(other, &value, &index_type); break; } @@ -2607,34 +2525,27 @@ INLINE bool sema_analyse_operator_method(SemaContext *context, Type *parent_type * 5. Register its external name. * 6. Add the module according to visibility. */ -static inline bool unit_add_method(SemaContext *context, Type *parent_type, Decl *method) +static inline bool type_add_method(SemaContext *context, Type *parent_type, Decl *method) { CompilationUnit *unit = context->unit; ASSERT(parent_type->canonical == parent_type); const char *name = method->name; - // Did we already define it externally? - Decl *other = sema_find_extension_method_in_list(unit->local_method_extensions, parent_type, name); - if (!other) other = sema_find_extension_method_in_list(unit->module->private_method_extensions, parent_type, name); - if (!other) other = sema_find_extension_method_in_list(compiler.context.method_extensions, parent_type, name); - if (other) - { - SEMA_ERROR(method, "This %s is already defined.", method_name_by_decl(method)); - SEMA_NOTE(other, "The previous definition was here."); - return false; - } - + // Attributes needs to be resolved early + bool erase_decl = false; + if (!sema_analyse_attributes(context, method, method->attributes, + method->decl_kind == DECL_MACRO ? ATTR_MACRO : ATTR_FUNC, &erase_decl)) return decl_poison(method); + if (erase_decl) return true; // Is it a base extension? - if (!type_is_user_defined(parent_type)) return unit_add_base_extension_method(context, unit, parent_type, method); + if (!type_is_user_defined(parent_type)) return unit_add_base_extension_method(context, method); // Resolve it as a user-defined type extension. Decl *parent = parent_type->decl; - Decl *ambiguous = NULL; - Decl *private = NULL; // If we found it, issue an error. - other = sema_resolve_method(unit, parent, name, &ambiguous, &private); + Decl *other = sema_resolve_method(parent, name); + if (!decl_ok(other)) return false; if (other) { if (unit->module->generic_module && other->unit->module->generic_module == unit->module->generic_module && other->unit->module != unit->module) @@ -2653,31 +2564,13 @@ static inline bool unit_add_method(SemaContext *context, Type *parent_type, Decl DEBUG_LOG("Method-like '%s.%s' analysed.", parent->name, method->name); - // Add it to the correct place: type methods, private extensions, local method extensions - switch (method->visibility) + Methods *table = parent->method_table; + if (!table) { - case VISIBLE_PUBLIC: - vec_add(parent->methods, method); - break; - case VISIBLE_PRIVATE: - if (parent->unit->module == unit->module && parent->visibility >= VISIBLE_PRIVATE) - { - vec_add(parent->methods, method); - break; - } - vec_add(unit->module->private_method_extensions, method); - break; - case VISIBLE_LOCAL: - if (parent->unit == unit && parent->visibility >= VISIBLE_LOCAL) - { - vec_add(parent->methods, method); - break; - } - vec_add(unit->local_method_extensions, method); - break; - default: - UNREACHABLE + table = parent->method_table = CALLOCS(Methods); + decltable_init(&table->method_table, 64); } + methods_add(table, method); return true; } @@ -2990,6 +2883,8 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl) // Is it an operator? if (decl->func_decl.operator) { + // We must resolve it here to avoid recursion. + decl->resolve_status = RESOLVE_DONE; if (!sema_analyse_operator_method(context, par_type, decl)) return false; } @@ -4355,6 +4250,7 @@ static bool sema_analyse_macro_method(SemaContext *context, Decl *decl) // Is it an operator? if (decl->func_decl.operator) { + decl->resolve_status = RESOLVE_DONE; if (!sema_analyse_operator_method(context, parent_type, decl)) return false; } @@ -4398,7 +4294,7 @@ INLINE bool sema_analyse_macro_body(SemaContext *context, Decl **body_parameters case VARDECL_ERASE: UNREACHABLE } - if (!sema_check_param_uniqueness_and_type(context, body_parameters, param, i, body_param_count)) return false; + if (!sema_check_param_uniqueness_and_type(context, body_parameters, param, i)) return false; param->resolve_status = RESOLVE_DONE; } return true; @@ -5090,14 +4986,13 @@ static bool sema_generate_parameterized_name_to_scratch(SemaContext *context, Mo if (fault) { type_mangle_introspect_name_to_buffer(fault->type->canonical); + scratch_buffer_append(mangled ? "_" : ":"); + scratch_buffer_append(fault->name); } else { scratch_buffer_append("null"); } - - scratch_buffer_append(mangled ? "_" : ":"); - scratch_buffer_append(fault->name); } else { @@ -5403,7 +5298,7 @@ bool sema_analyse_method_register(SemaContext *context, Decl *method) RETURN_SEMA_ERROR(parent_type_info, "Methods can not be associated with '%s'", type_to_error_string(parent_type)); } - return unit_add_method(context, parent_type->canonical, method); + return type_add_method(context, parent_type->canonical, method); } bool sema_analyse_decl(SemaContext *context, Decl *decl) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index ac75e2861..3f7ce5cb4 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3616,7 +3616,7 @@ static Expr *sema_expr_find_subscript_type_or_overload_for_subscript(SemaContext Decl **overload_ptr) { Decl *overload = NULL; - overload = sema_find_untyped_operator(context, current_expr->type, overload_type, NULL); + overload = sema_find_untyped_operator(current_expr->type, overload_type, NULL); if (overload) { // Overload for []= @@ -3833,7 +3833,7 @@ static inline bool sema_expr_analyse_subscript_lvalue(SemaContext *context, Expr { if (start_from_end) { - Decl *len = sema_find_untyped_operator(context, current_expr->type, OVERLOAD_LEN, NULL); + Decl *len = sema_find_untyped_operator(current_expr->type, OVERLOAD_LEN, NULL); if (!len) { if (check_valid) goto VALID_FAIL_POISON; @@ -3948,7 +3948,7 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, { if (start_from_end) { - Decl *len = sema_find_untyped_operator(context, current_expr->type, OVERLOAD_LEN, NULL); + Decl *len = sema_find_untyped_operator(current_expr->type, OVERLOAD_LEN, NULL); if (!len) { if (check_valid) goto VALID_FAIL_POISON; @@ -4621,35 +4621,6 @@ static inline bool sema_analyse_macro_func_access(SemaContext *context, Expr *ex return sema_expr_analyse_type_access(context, expr, parent->type, identifier, missing_ref); } -static inline Decl *sema_check_for_type_method(SemaContext *context, Expr *expr, Type *parent_type, const char *name, bool *missing_ref) -{ - ASSERT(parent_type == parent_type->canonical); - Decl *ambiguous = NULL; - Decl *private = NULL; - Decl *member = sema_resolve_type_method(context->unit, parent_type, name, &ambiguous, &private); - if (private) - { - if (missing_ref) - { - *missing_ref = true; - } - else - { - SEMA_ERROR(expr, "The method '%s' has private visibility.", name); - } - - return poisoned_decl; - } - if (ambiguous) - { - SEMA_ERROR(expr, "'%s' is an ambiguous name and so cannot be resolved, " - "it may refer to method defined in '%s' or one in '%s'", - name, member->unit->module->name->module, ambiguous->unit->module->name->module); - return poisoned_decl; - } - return member; -} - static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *expr, Type *parent_type, Expr *identifier, bool *missing_ref) { ASSERT_SPAN(expr, identifier->expr_kind == EXPR_UNRESOLVED_IDENTIFIER); @@ -4668,7 +4639,7 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp if (!type_may_have_sub_elements(canonical)) { - Decl *member = sema_check_for_type_method(context, expr, parent_type->canonical, name, missing_ref); + Decl *member = sema_resolve_type_method(parent_type->canonical, name); if (!decl_ok(member)) return false; if (!member) { @@ -4706,11 +4677,11 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp UNREACHABLE } - Decl *member = sema_decl_stack_find_decl_member(context, decl, name, METHODS_AND_FIELDS); + Decl *member = sema_decl_stack_find_decl_member(context, decl, name, FIELDS_ONLY); if (!decl_ok(member)) return false; if (!member) { - member = sema_check_for_type_method(context, expr, decl->type, name, missing_ref); + member = sema_resolve_type_method(decl->type, name); if (!decl_ok(member)) return false; } if (!member) @@ -5192,12 +5163,13 @@ CONTINUE: sema_append_interface_methods(decl, &method_exprs, expr->span); } // Look through natively defined methods. - append_to_method_list(decl->methods, &method_exprs, expr->span); + Methods *methods = decl->method_table; + if (methods) append_to_method_list(methods->methods, &method_exprs, expr->span); + } + else + { + append_extension_methods(type, compiler.context.method_extension_list, &method_exprs, expr->span); } - append_extension_methods(type, context->unit->local_method_extensions, &method_exprs, expr->span); - append_extension_methods(type, context->unit->module->private_method_extensions, &method_exprs, expr->span); - append_extension_methods(type, compiler.context.method_extensions, &method_exprs, expr->span); - type = type_find_parent_type(type); if (type) goto CONTINUE; expr_rewrite_const_untyped_list(expr, method_exprs); @@ -6128,20 +6100,7 @@ CHECK_DEEPER: // 9. At this point we may only have distinct, struct, union, error, enum, interface if (!type_may_have_sub_elements(type)) { - Decl *ambiguous = NULL; - Decl *private = NULL; - Decl *method = sema_resolve_type_method(context->unit, type, kw, &ambiguous, &private); - if (private) - { - if (missing_ref) goto MISSING_REF; - RETURN_SEMA_ERROR(expr, "The method '%s' has private visibility.", kw); - } - if (ambiguous) - { - RETURN_SEMA_ERROR(expr, "'%s' is an ambiguous name and so cannot be resolved, " - "it may refer to method defined in '%s' or one in '%s'", - kw, method->unit->module->name->module, ambiguous->unit->module->name->module); - } + Decl *method = sema_resolve_type_method(type, kw); if (!method) { if (missing_ref) goto MISSING_REF; @@ -6159,7 +6118,7 @@ CHECK_DEEPER: // 10. Dump all members and methods into a decl stack. Decl *decl = type->decl; - Decl *member = sema_decl_stack_find_decl_member(context, decl, kw, METHODS_AND_FIELDS); + Decl *member = sema_decl_stack_find_decl_member(context, decl, kw, METHODS_INTERFACES_AND_FIELDS); if (!decl_ok(member)) return false; if (member && decl->decl_kind == DECL_ENUM && member->decl_kind == DECL_VAR && sema_cast_const(parent)) @@ -6174,24 +6133,22 @@ CHECK_DEEPER: return true; } Decl *private = NULL; - if (!member) + if (!member && decl->decl_kind == DECL_INTERFACE) { - Decl *ambiguous = NULL; - member = sema_resolve_method(context->unit, decl, kw, &ambiguous, &private); - // Look at interface parents - if (!member && decl->decl_kind == DECL_INTERFACE) + Decl *inf; + FOREACH(TypeInfo *, parent_interface, decl->interfaces) { - FOREACH(TypeInfo *, parent_interface, decl->interfaces) + if (!sema_resolve_type_info(context, parent_interface, RESOLVE_TYPE_NO_CHECK_DISTINCT)) return false; + Decl *parent_decl = parent_interface->type->decl; + Decl *value = sema_resolve_method(parent_decl, kw); + if (value && member) { - member = sema_resolve_method(context->unit, parent_interface->type->decl, kw, &ambiguous, &private); - if (member) break; + RETURN_SEMA_ERROR(expr, "Ambiguous method '%s' on '%s', it was implemented on both '%s' and '%s'.", kw, + type_to_error_string(parent->type), + inf->name, parent_decl->name); } - } - if (ambiguous) - { - ASSERT(member); - RETURN_SEMA_ERROR(expr, "'%s' is an ambiguous name and so cannot be resolved, it may refer to method defined in '%s' or one in '%s'", - kw, member->unit->module->name->module, ambiguous->unit->module->name->module); + member = value; + inf = parent_decl; } } @@ -6875,7 +6832,7 @@ INLINE bool sema_rewrite_op_assign(SemaContext *context, Expr *expr, Expr *left, { Expr *parent = exprptr(left->subscript_assign_expr.expr); Type *parent_type = type_no_optional(parent->type)->canonical; - Decl *operator = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_REF, NULL); + Decl *operator = sema_find_untyped_operator(parent_type, OVERLOAD_ELEMENT_REF, NULL); Expr *index = exprptr(left->subscript_assign_expr.index); if (operator) { @@ -6885,7 +6842,7 @@ INLINE bool sema_rewrite_op_assign(SemaContext *context, Expr *expr, Expr *left, goto AFTER_ADDR; } // If we only have []=, then we need [] - operator = sema_find_untyped_operator(context, parent_type, OVERLOAD_ELEMENT_AT, NULL); + operator = sema_find_untyped_operator(parent_type, OVERLOAD_ELEMENT_AT, NULL); if (!operator) { RETURN_SEMA_ERROR(left, "There is no overload for [] for %s.", type_quoted_error_string(type_no_optional(left->type))); @@ -8528,7 +8485,7 @@ static inline bool sema_expr_analyse_neg_plus(SemaContext *context, Expr *expr) // Check for overload if (type_is_user_defined(canonical)) { - Decl *overload = sema_find_untyped_operator(context, canonical, OVERLOAD_UNARY_MINUS, NULL); + Decl *overload = sema_find_untyped_operator(canonical, OVERLOAD_UNARY_MINUS, NULL); if (overload) { // Plus just returns inner @@ -8604,7 +8561,7 @@ static inline bool sema_expr_analyse_bit_not(SemaContext *context, Expr *expr, b if (type_is_user_defined(canonical) && canonical->type_kind != TYPE_BITSTRUCT) { - Decl *overload = sema_find_untyped_operator(context, canonical, OVERLOAD_NEGATE, NULL); + Decl *overload = sema_find_untyped_operator(canonical, OVERLOAD_NEGATE, NULL); if (overload) return sema_insert_method_call(context, expr, overload, inner, NULL, false); } @@ -8780,7 +8737,7 @@ static bool sema_analyse_assign_mutate_overloaded_subscript(SemaContext *context { Expr *increased = exprptr(subscript_expr->subscript_assign_expr.expr); Type *type_check = increased->type->canonical; - Decl *operator = sema_find_untyped_operator(context, type_check, OVERLOAD_ELEMENT_REF, NULL); + Decl *operator = sema_find_untyped_operator(type_check, OVERLOAD_ELEMENT_REF, NULL); Expr **args = NULL; // The simple case: we have &[] so just replace it by that. if (operator) @@ -8792,7 +8749,7 @@ static bool sema_analyse_assign_mutate_overloaded_subscript(SemaContext *context return true; } // We need []= and [] now. - operator = sema_find_untyped_operator(context, type_check, OVERLOAD_ELEMENT_AT, NULL); + operator = sema_find_untyped_operator(type_check, OVERLOAD_ELEMENT_AT, NULL); if (!operator) { RETURN_SEMA_ERROR(main, "There is no overload for [] for %s.", type_quoted_error_string(increased->type)); @@ -9615,15 +9572,6 @@ static inline bool sema_expr_analyse_decl_element(SemaContext *context, Designat if (!decl_ok(member)) return false; if (!member) { - Decl *ambiguous = NULL; - Decl *private = NULL; - member = sema_resolve_method(context->unit, actual_type->decl, kw, &ambiguous, &private); - if (ambiguous) - { - sema_error_at(context, loc, "'%s' is an ambiguous name and so cannot be resolved, it may refer to method defined in '%s' or one in '%s'", - kw, member->unit->module->name->module, ambiguous->unit->module->name->module); - return false; - } if (is_missing) { *is_missing = true; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 6484717e4..c322bdd35 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -100,9 +100,10 @@ bool sema_analyse_ct_expr(SemaContext *context, Expr *expr); Decl *sema_find_typed_operator(SemaContext *context, OperatorOverload operator_overload, SourceSpan span, Expr *lhs, Expr *rhs, bool *reverse); OverloadMatch sema_find_typed_operator_type(SemaContext *context, OperatorOverload operator_overload, OverloadType overloat_type, Type *lhs_type, Type *rhs_type, Expr *rhs, Decl **candidate_ref, OverloadMatch last_match, Decl **ambiguous_ref); BoolErr sema_type_has_equality_overload(SemaContext *context, Type *type); -Decl *sema_find_untyped_operator(SemaContext *context, Type *type, OperatorOverload operator_overload, Decl *skipped); +Decl *sema_find_untyped_operator(Type *type, OperatorOverload operator_overload, Decl *skipped); bool sema_insert_method_call(SemaContext *context, Expr *method_call, Decl *method_decl, Expr *parent, Expr **arguments, bool reverse_overload); bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr); +void sema_add_methods_to_decl_stack(SemaContext *context, Decl *decl); bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool call_var_optional, bool *no_match_ref); Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, unsigned *index_ref); diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index b461636b9..e4018dc17 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -523,7 +523,7 @@ void sema_trace_liveness(void) } bool keep_tests = compiler.build.testing; bool keep_benchmarks = compiler.build.benchmarking; - FOREACH(Decl *, function, compiler.context.method_extensions) + FOREACH(Decl *, function, compiler.context.method_extension_list) { if (function->func_decl.attr_dynamic) function->no_strip = true; if (function->is_export || function->no_strip) sema_trace_decl_liveness(function); @@ -548,10 +548,6 @@ void sema_trace_liveness(void) { if (var->is_export || var->no_strip) sema_trace_decl_liveness(var); } - FOREACH(Decl *, method, unit->local_method_extensions) - { - if (method->is_export || method->no_strip) sema_trace_decl_liveness(method); - } } } } @@ -566,7 +562,9 @@ INLINE void sema_trace_enum_associated(Decl *decl) } INLINE void sema_trace_decl_dynamic_methods(Decl *decl) { - Decl **methods = decl->methods; + Methods *table = decl->method_table; + if (!table) return; + Decl **methods = table->methods; unsigned method_count = vec_size(methods); if (!method_count) return; for (unsigned i = 0; i < method_count; i++) diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index f5fcf2c30..307191919 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -82,13 +82,7 @@ static bool add_interface_to_decl_stack(SemaContext *context, Decl *decl) static bool add_members_to_decl_stack(SemaContext *context, Decl *decl, FindMember find) { - if (find != FIELDS_ONLY) - { - FOREACH(Decl *, func, decl->methods) - { - sema_decl_stack_push(func); - } - } + if (find != FIELDS_ONLY) sema_add_methods_to_decl_stack(context, decl); while (decl->decl_kind == DECL_DISTINCT) { Type *type = decl->distinct->type->canonical; @@ -124,7 +118,12 @@ Decl *sema_decl_stack_find_decl_member(SemaContext *context, Decl *decl_owner, c if (!add_members_to_decl_stack(context, decl_owner, find)) return poisoned_decl; Decl *member = sema_decl_stack_resolve_symbol(symbol); sema_decl_stack_restore(state); - return member; + if (member || find == FIELDS_ONLY) return member; + if (find == METHODS_AND_FIELDS) + { + return sema_resolve_method_only(decl_owner, symbol); + } + return sema_resolve_method(decl_owner, symbol); } static inline Decl *sema_find_decl_in_module(Module *module, Path *path, const char *symbol, Module **path_found_ref) @@ -839,56 +838,36 @@ Decl *sema_find_extension_method_in_list(Decl **extensions, Type *type, const ch return NULL; } - - -Decl *sema_resolve_method_in_module(Module *module, Type *actual_type, const char *method_name, - Decl **private_found, Decl **ambiguous, MethodSearchType search_type) +Decl *sema_resolve_method_only(Decl *type, const char *method_name) { - if (module->is_generic) return NULL; - Decl *found = sema_find_extension_method_in_list(module->private_method_extensions, actual_type, method_name); - // The found one might not be visible - if (found && search_type < METHOD_SEARCH_CURRENT && found->visibility == VISIBLE_PRIVATE) + Methods *methods = type->method_table; + Decl *found = NULL; + if (methods) { - *private_found = found; - found = NULL; + found = declptrzero(decltable_get(&methods->method_table, method_name)); } - assert(!found || found->visibility != VISIBLE_LOCAL); - if (found && search_type == METHOD_SEARCH_CURRENT) return found; - // We are now searching submodules, so hide the private ones. - if (search_type == METHOD_SEARCH_CURRENT) search_type = METHOD_SEARCH_SUBMODULE_CURRENT; - FOREACH(Module *, mod, module->sub_modules) - { - Decl *new_found = sema_resolve_method_in_module(mod, actual_type, method_name, private_found, ambiguous, - search_type); - if (!new_found) continue; - if (found) - { - *ambiguous = new_found; - return found; - } - found = new_found; - } - // We might have it ambiguous due to searching sub modules. return found; } -Decl *sema_resolve_method(CompilationUnit *unit, Decl *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref) +Decl *sema_resolve_method(Decl *type, const char *method_name) { // Interface, prefer interface methods. - if (type->decl_kind == DECL_INTERFACE) + bool is_interface = type->decl_kind == DECL_INTERFACE; + if (is_interface) { FOREACH(Decl *, method, type->interface_methods) { if (method_name == method->name) return method; } } - // Look through natively defined methods. - FOREACH(Decl *, method, type->methods) - { - if (method_name == method->name) return method; - } - return sema_resolve_type_method(unit, type->type, method_name, ambiguous_ref, private_ref); + Methods *methods = type->method_table; + Decl *found = NULL; + if (methods) + { + found = declptrzero(decltable_get(&methods->method_table, method_name)); + } + return found; } bool sema_check_type_variable_array(SemaContext *context, TypeInfo *type_info) @@ -975,75 +954,41 @@ bool sema_resolve_type_decl(SemaContext *context, Type *type) UNREACHABLE } -Decl *sema_resolve_type_method(CompilationUnit *unit, Type *type, const char *method_name, Decl **ambiguous_ref, Decl **private_ref) +Decl *sema_resolve_type_method(CanonicalType *type, const char *method_name) { - ASSERT(type == type->canonical); - Decl *private = NULL; - Decl *ambiguous = NULL; - Decl *found = sema_find_extension_method_in_list(unit->local_method_extensions, type, method_name); - if (!found) found = sema_resolve_method_in_module(unit->module, type, method_name, &private, &ambiguous, METHOD_SEARCH_CURRENT); - if (ambiguous) + RETRY: + if (!type_is_user_defined(type)) { - *ambiguous_ref = ambiguous; - ASSERT(found); - return found; - } - - // 2. Lookup in imports - FOREACH(Decl *, import, unit->imports) - { - if (import->import.module->is_generic) continue; - - Decl *new_found = sema_resolve_method_in_module(import->import.module, type, method_name, - &private, &ambiguous, - import->import.import_private_as_public - ? METHOD_SEARCH_PRIVATE_IMPORTED - : METHOD_SEARCH_IMPORTED); - if (!new_found || found == new_found) continue; - if (found) + Decl *found = declptrzero(methodtable_get(&compiler.context.method_extensions, type, method_name)); + if (found) return found; + switch (type->type_kind) { - *ambiguous_ref = new_found; - return found; - } - found = new_found; - if (ambiguous) - { - *ambiguous_ref = ambiguous; - return found; + case TYPE_ARRAY: + return declptrzero(methodtable_get(&compiler.context.method_extensions, type_get_inferred_array(type->array.base), method_name)); + case TYPE_VECTOR: + return declptrzero(methodtable_get(&compiler.context.method_extensions, type_get_inferred_vector(type->array.base), method_name)); + default: + return NULL; } } - if (!found) + Decl *type_decl = type->decl; + Methods *methods = type_decl->method_table; + Decl *found = methods ? declptrzero(decltable_get(&methods->method_table, method_name)) : NULL; + if (found || !type_decl->is_substruct) return found; + switch (type->type_kind) { - found = sema_resolve_method_in_module(compiler.context.core_module, type, method_name, - &private, &ambiguous, METHOD_SEARCH_IMPORTED); + case TYPE_STRUCT: + type = type_decl->strukt.members[0]->type->canonical; + goto RETRY; + case TYPE_DISTINCT: + type = type_decl->distinct->type->canonical; + goto RETRY; + case TYPE_ENUM: + type = type_decl->enums.type_info->type->canonical; + goto RETRY; + default: + UNREACHABLE } - if (found && ambiguous) - { - *ambiguous_ref = ambiguous; - return found; - } - if (!found) - { - found = sema_find_extension_method_in_list(compiler.context.method_extensions, type, method_name); - private = NULL; - } - if (private) *private_ref = private; - if (!found) - { - if (type->type_kind == TYPE_ARRAY) - { - Type *inferred_array = type_get_inferred_array(type->array.base); - found = sema_resolve_type_method(unit, inferred_array, method_name, ambiguous_ref, private_ref); - if (found) *private_ref = NULL; - } - else if (type->type_kind == TYPE_VECTOR) - { - Type *inferred_vector = type_get_inferred_vector(type->array.base); - found = sema_resolve_type_method(unit, inferred_vector, method_name, ambiguous_ref, private_ref); - if (found) *private_ref = NULL; - } - } - return found; } bool unit_resolve_parameterized_symbol(SemaContext *context, NameResolve *name_resolve) diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 14150c287..d1db86bb1 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -749,7 +749,7 @@ static bool sema_check_interface(SemaContext *context, Decl *decl, TypeInfo *int static inline bool sema_check_interfaces(SemaContext *context, Decl *decl) { Decl **store = sema_decl_stack_store(); - FOREACH(Decl *, method, decl->methods) sema_decl_stack_push(method); + sema_add_methods_to_decl_stack(context, decl); FOREACH(TypeInfo *, interface_type, decl->interfaces) { if (!sema_check_interface(context, decl, interface_type, interface_type)) diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 797b08ff0..8ec6dc5c4 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -1574,11 +1574,11 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen if (!value_type || canonical->type_kind == TYPE_DISTINCT) { // Get the overload for .len - len = sema_find_untyped_operator(context, enumerator->type, OVERLOAD_LEN, NULL); + len = sema_find_untyped_operator(enumerator->type, OVERLOAD_LEN, NULL); // For foo[] - Decl *by_val = sema_find_untyped_operator(context, enumerator->type, OVERLOAD_ELEMENT_AT, NULL); + Decl *by_val = sema_find_untyped_operator(enumerator->type, OVERLOAD_ELEMENT_AT, NULL); // For &foo[] - Decl *by_ref = sema_find_untyped_operator(context, enumerator->type, OVERLOAD_ELEMENT_REF, NULL); + Decl *by_ref = sema_find_untyped_operator(enumerator->type, OVERLOAD_ELEMENT_REF, NULL); // If we don't have .len, or there is neither by val nor by ref if (!len || (!by_val && !by_ref)) diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 318601d08..9fdfedd76 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -118,6 +118,27 @@ void context_pop_defers(SemaContext *context, AstId *next) context->active_scope.defer_last = defer_start; } +void sema_add_methods_to_decl_stack(SemaContext *context, Decl *decl) +{ + if (!decl->method_table) return; + FOREACH(Decl *, func, decl->method_table->methods) + { + switch (func->visibility) + { + case VISIBLE_LOCAL: + if (context->unit != func->unit) continue; + break; + case VISIBLE_PRIVATE: + if (context->unit->module != func->unit->module) continue; + break; + default: + break; + } + sema_decl_stack_push(func); + } +} + + void context_pop_defers_and_replace_ast(SemaContext *context, Ast *ast) { diff --git a/src/compiler/types.c b/src/compiler/types.c index 28d3ae3ef..e1fd23611 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1058,23 +1058,6 @@ bool type_is_structurally_equivalent(Type *type1, Type *type2) return true; } -bool type_is_user_defined(Type *type) -{ - switch (type->type_kind) - { - case TYPE_ENUM: - case TYPE_FUNC_RAW: - case TYPE_STRUCT: - case TYPE_UNION: - case TYPE_DISTINCT: - case TYPE_BITSTRUCT: - case TYPE_TYPEDEF: - case TYPE_INTERFACE: - return true; - default: - return false; - } -} Type *type_get_indexed_type(Type *type) { diff --git a/test/test_suite/methods/extending_with_visibility_fail.c3 b/test/test_suite/methods/extending_with_visibility_fail.c3 index 548ad1beb..2e1104ee8 100644 --- a/test/test_suite/methods/extending_with_visibility_fail.c3 +++ b/test/test_suite/methods/extending_with_visibility_fail.c3 @@ -27,11 +27,11 @@ Bar y = { 1 }; fn int test() { - x.get1(); // #error: method + x.get1(); x.get2(); - x.get3(); // #error: method - y.get1(); // #error: method + x.get3(); + y.get1(); y.get2(); - y.get3(); // #error: method + y.get3(); return 1; } diff --git a/test/test_suite/methods/extending_with_visibility_fail_private.c3 b/test/test_suite/methods/extending_with_visibility_fail_private.c3 index 89b169507..301ce5c61 100644 --- a/test/test_suite/methods/extending_with_visibility_fail_private.c3 +++ b/test/test_suite/methods/extending_with_visibility_fail_private.c3 @@ -29,9 +29,9 @@ fn int test() { x.get1(); x.get2(); - x.get3(); // #error: method + x.get3(); y.get1(); y.get2(); - y.get3(); // #error: method + y.get3(); return 1; } diff --git a/test/test_suite/methods/operator_mismatch.c3 b/test/test_suite/methods/operator_mismatch.c3 index d0f96160b..f04116da2 100644 --- a/test/test_suite/methods/operator_mismatch.c3 +++ b/test/test_suite/methods/operator_mismatch.c3 @@ -1,12 +1,15 @@ struct Foo { int a; } struct Foo2 { int a; } +struct Foo3 { int a; } struct Bar { int a; } fn int Foo.x(&self, int x) @operator([]) => 1; fn int Foo.y(&self, int x) @operator(&[]) => 1; // #error: The return type must be a pointer fn int Foo2.y(&self, int x) @operator([]) => null; fn int** Foo2.y2(&self, int x) @operator(&[]) => null; // #error: There is a mismatch of the 'value' type -fn void Foo.z(&self, uint x, int a) @operator([]=) {} // #error: There is a mismatch of the 'index' + +fn int Foo3.x(&self, int x) @operator([]) => 1; +fn void Foo3.z(&self, uint x, int a) @operator([]=) {} // #error: There is a mismatch of the 'index' fn double Bar.x(&self, int x) @operator([]) => 0.1; fn void Bar.y(&self, int x, float y) @operator([]=) {} // #error: There is a mismatch of the 'value'