diff --git a/resources/lib/std/list.c3 b/resources/lib/std/list.c3 index 67b69f3fe..1b2934970 100644 --- a/resources/lib/std/list.c3 +++ b/resources/lib/std/list.c3 @@ -99,7 +99,7 @@ fn bool List.isEmpty(List *list) return list.size; } -fn usize List.len(List *list) +fn usize List.len(List *list) @operator(len) { return list.size; } @@ -116,12 +116,13 @@ fn void List.free(List *list) list.size = 0; } -macro usize List.operator_len(List &list) -{ - return list.len(); -} -macro Type List.operator_element_at(List &list, usize index) +macro Type List.item_at(List &list, usize index) @operator(elementat) { return list.entries[index]; } + +macro Type* List.item_ref(List &list, usize index) @operator(elementref) +{ + return &list.entries[index]; +} diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 3671ee2ad..0bba3dfe9 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -313,6 +313,7 @@ typedef struct { Expr *expr; uint32_t alignment; + OperatorOverload operator; }; } Attr; @@ -588,6 +589,7 @@ typedef struct Decl_ bool escaping : 1; bool is_value : 1; bool is_autoimport : 1; + OperatorOverload operator : 3; union { void *backend_ref; @@ -1607,10 +1609,9 @@ extern const char *kw_distinct; extern const char *kw_ensure; extern const char *kw_inline; extern const char *kw_inf; -extern const char *kw_iterator; -extern const char *kw_operator_element_at; -extern const char *kw_operator_element_at_ref; -extern const char *kw_operator_len; +extern const char *kw_elementat; +extern const char *kw_elementref; +extern const char *kw_elementset; extern const char *kw_len; extern const char *kw_next; extern const char *kw_nan; diff --git a/src/compiler/context.c b/src/compiler/context.c index b85a6215c..a593ba5b3 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -123,6 +123,7 @@ bool context_set_module(ParseContext *context, Path *path, TokenId *generic_para void unit_register_external_symbol(CompilationUnit *unit, Decl *decl) { + if (decl->decl_kind == DECL_MACRO) return; assert(decl->external_name && "Missing external name"); Decl *prev = stable_get(&unit->external_symbols, decl->external_name); if (prev) return; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index f715d60c4..e92760b10 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -637,6 +637,14 @@ typedef enum ATTR_MACRO = 1 << 13, } AttributeDomain; +typedef enum +{ + OVERLOAD_ELEMENT_AT = 1, + OVERLOAD_ELEMENT_REF, + OVERLOAT_ELEMENT_SET, + OVERLOAD_LEN +} OperatorOverload; + typedef enum { ATTRIBUTE_INLINE, @@ -661,6 +669,7 @@ typedef enum ATTRIBUTE_FASTCALL, ATTRIBUTE_OVERLAP, ATTRIBUTE_AUTOIMPORT, + ATTRIBUTE_OPERATOR, ATTRIBUTE_NONE, NUMBER_OF_ATTRIBUTES = ATTRIBUTE_NONE, } AttributeType; diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 90faf489a..14499f6ea 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -942,34 +942,47 @@ static bool sema_analyse_operator_common(Decl *method, TypeInfo **rtype_ptr, Dec return true; } -Decl *sema_find_operator(SemaContext *context, Expr *expr, const char *kw) + +Decl *sema_find_operator(SemaContext *context, Expr *expr, OperatorOverload operator_overload) { Decl *ambiguous = NULL; Decl *private = NULL; Type *type = expr->type->canonical; - Decl *method = type_may_have_sub_elements(type) ? sema_resolve_method(context->unit, type->decl, kw, &ambiguous, &private) : NULL; - if (!decl_ok(method)) return NULL; - if (!method) + if (!type_may_have_sub_elements(type)) return NULL; + Decl *def = type->decl; + Decl **funcs = def->methods; + VECEACH(funcs, i) { - if (ambiguous) + Decl *func = funcs[i]; + if (func->operator == operator_overload) { - SEMA_ERROR(expr, - "It's not possible to find a definition for '%s' on %s that is not ambiguous.", - kw, type_quoted_error_string(expr->type)); - return NULL; + unit_register_external_symbol(context->compilation_unit, func); + return func; } - if (private) - { - SEMA_ERROR(expr, - "It's not possible to find a public definition for '%s' with '%s'.", - kw, type_quoted_error_string(expr->type)); - return NULL; - } - return NULL; } - return method; + Decl **imports = context->unit->imports; + VECEACH(imports, i) + { + Decl *import = imports[i]; + Module *module = import->module; + + if (module->is_generic) continue; + + Decl **extensions = module->method_extensions; + VECEACH(extensions, j) + { + Decl *extension = extensions[j]; + if (extension->operator == operator_overload) + { + unit_register_external_symbol(context->compilation_unit, extension); + return extension; + } + } + } + return NULL; } + static inline bool sema_analyse_operator_element_at(Decl *method) { TypeInfo *rtype; @@ -983,6 +996,19 @@ static inline bool sema_analyse_operator_element_at(Decl *method) return true; } +static inline bool sema_analyse_operator_element_set(Decl *method) +{ + TypeInfo *rtype; + Decl **params; + if (!sema_analyse_operator_common(method, &rtype, ¶ms, 3)) return false; + if (rtype->type->canonical != type_void) + { + SEMA_ERROR(rtype, "The return type should be 'void'."); + return false; + } + return true; +} + static inline bool sema_analyse_operator_len(Decl *method) { TypeInfo *rtype; @@ -998,15 +1024,18 @@ static inline bool sema_analyse_operator_len(Decl *method) static bool sema_check_operator_method_validity(Decl *method) { - if (method->name == kw_operator_element_at || method->name == kw_operator_element_at_ref) + switch (method->operator) { - return sema_analyse_operator_element_at(method); + case OVERLOAT_ELEMENT_SET: + return sema_analyse_operator_element_set(method); + case OVERLOAD_ELEMENT_AT: + case OVERLOAD_ELEMENT_REF: + return sema_analyse_operator_element_at(method); + case OVERLOAD_LEN: + return sema_analyse_operator_len(method); + default: + UNREACHABLE } - if (method->name == kw_operator_len) - { - return sema_analyse_operator_len(method); - } - return true; } static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type, Decl *method_like) @@ -1030,7 +1059,8 @@ static inline bool unit_add_method_like(CompilationUnit *unit, Type *parent_type SEMA_TOKID_PREV(method->name_token, "The previous definition was here."); return false; } - if (!sema_check_operator_method_validity(method_like)) return false; + if (method_like->operator && !sema_check_operator_method_validity(method_like)) return false; + REMINDER("Check multiple operator"); scratch_buffer_clear(); if (method_like->visibility <= VISIBLE_MODULE) { @@ -1150,6 +1180,7 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute [ATTRIBUTE_NOSCOPE] = ATTR_MACRO, [ATTRIBUTE_ESCAPING] = ATTR_MACRO, [ATTRIBUTE_AUTOIMPORT] = ATTR_MACRO | ATTR_FUNC, + [ATTRIBUTE_OPERATOR] = ATTR_MACRO | ATTR_FUNC, }; if ((attribute_domain[type] & domain) != domain) @@ -1165,6 +1196,38 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute case ATTRIBUTE_VECCALL: case ATTRIBUTE_REGCALL: return type; + case ATTRIBUTE_OPERATOR: + { + Expr *expr = attr->expr; + if (!expr || expr->expr_kind != EXPR_IDENTIFIER) goto FAILED_OP_TYPE; + if (expr->identifier_expr.path) goto FAILED_OP_TYPE; + TokenId tok = expr->identifier_expr.identifier; + const char *kw = TOKSTR(tok); + if (kw == kw_elementat) + { + attr->operator = OVERLOAD_ELEMENT_AT; + } + else if (kw == kw_elementref) + { + attr->operator = OVERLOAD_ELEMENT_REF; + } + else if (kw == kw_elementset) + { + attr->operator = OVERLOAT_ELEMENT_SET; + } + else if (kw == kw_len) + { + attr->operator = OVERLOAD_LEN; + } + else + { + goto FAILED_OP_TYPE; + } + return ATTRIBUTE_OPERATOR; + FAILED_OP_TYPE: + SEMA_ERROR(expr, "'operator' requires an operator type argument: '%s', '%s' or '%s'.", kw_elementat, kw_elementref, kw_len); + return ATTRIBUTE_NONE; + } case ATTRIBUTE_ALIGN: if (!attr->expr) { @@ -1389,6 +1452,7 @@ static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl) context->unit->main_function = function; return true; } + static inline bool sema_analyse_func(SemaContext *context, Decl *decl) { DEBUG_LOG("----Analysing function %s", decl->name); @@ -1406,6 +1470,10 @@ static inline bool sema_analyse_func(SemaContext *context, Decl *decl) switch (attribute) { + case ATTRIBUTE_OPERATOR: + had = decl->operator > 0; + decl->operator = attr->operator; + break; case ATTRIBUTE_EXTNAME: had = decl->extname != NULL; decl->extname = attr->expr->const_expr.string.chars; @@ -1559,6 +1627,10 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) bool had = false; switch (attribute) { + case ATTRIBUTE_OPERATOR: + had = decl->operator > 0; + decl->operator = attr->operator; + break; case ATTRIBUTE_NOSCOPE: had = decl->no_scope; decl->no_scope = true; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 5d856ae52..d8e8df6fd 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -2572,14 +2572,14 @@ static inline bool sema_expr_analyse_subscript(SemaContext *context, Expr *expr, if (!inner_type) { Decl *decl = NULL; - if (is_addr) decl = sema_find_operator(context, current_expr, kw_operator_element_at_ref); + if (is_addr) decl = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_REF); if (!decl) { - decl = sema_find_operator(context, current_expr, kw_operator_element_at); + decl = sema_find_operator(context, current_expr, OVERLOAD_ELEMENT_AT); if (decl && is_addr) { - SEMA_ERROR(expr, "'%s' is not defined for %s, so you need && to take the address of the temporary.", - kw_operator_element_at_ref, type_quoted_error_string(current_expr->type)); + SEMA_ERROR(expr, "A function or macro with '@operator(%s)' is not defined for %s, so you need && to take the address of the temporary.", + kw_elementref, type_quoted_error_string(current_expr->type)); return false; } } diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 68da352dc..fad4c15a5 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -66,7 +66,7 @@ void sema_analysis_pass_decls(Module *module); void sema_analysis_pass_ct_assert(Module *module); void sema_analysis_pass_functions(Module *module); void sema_analyze_stage(Module *module, AnalysisStage stage); -Decl *sema_find_operator(SemaContext *context, Expr *expr, const char *kw); +Decl *sema_find_operator(SemaContext *context, Expr *expr, OperatorOverload operator_overload); bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr); bool sema_analyse_ct_expr(SemaContext *context, Expr *expr); bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index c9327e92d..25eb8df73 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -890,158 +890,22 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement) } -static inline bool sema_inline_default_iterator(SemaContext *context, Expr *expr, Decl *decl) -{ - Expr *inner = expr_copy(expr); - expr_insert_addr(inner); - expr->expr_kind = EXPR_CALL; - expr->call_expr = (ExprCall) { - .is_type_method = true, - }; - REMINDER("Failability"); - return sema_expr_analyse_general_call(context, expr, decl, inner, decl->decl_kind == DECL_MACRO, false); -} -static Decl *find_iterator(SemaContext *context, Expr *enumerator) -{ - if (!type_may_have_sub_elements(enumerator->type)) - { - SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type %s.", type_quoted_error_string(enumerator->type)); - return NULL; - } - Decl *ambiguous = NULL; - Decl *private = NULL; - Decl *method = sema_resolve_method(context->unit, enumerator->type->decl, kw_iterator, &ambiguous, &private); - if (!decl_ok(method)) return NULL; - if (!method) - { - if (ambiguous) - { - SEMA_ERROR(enumerator, - "It's not possible to find a definition for 'iterator' on %s that is not ambiguous.", - type_quoted_error_string(enumerator->type)); - return NULL; - } - if (private) - { - SEMA_ERROR(enumerator, - "It's not possible to find a public definition for 'iterator' with '%s'.", - type_quoted_error_string(enumerator->type)); - return NULL; - } - SEMA_ERROR(enumerator, - "This type cannot be iterated over, implement a method or method macro called 'iterator'.", - type_to_error_string(enumerator->type)); - return NULL; - } - Decl **parameters; - TypeInfo *iterator_type; - switch (method->decl_kind) - { - case DECL_GENERIC: - parameters = method->generic_decl.parameters; - iterator_type = method->generic_decl.rtype; - break; - case DECL_MACRO: - parameters = method->macro_decl.parameters; - iterator_type = method->macro_decl.rtype; - break; - case DECL_FUNC: - parameters = method->func_decl.function_signature.params; - iterator_type = method->func_decl.function_signature.returntype; - break; - default: - UNREACHABLE - } - if (vec_size(parameters) > 1) - { - SEMA_ERROR(enumerator, "'iterator()' takes parameters and can't be used for 'foreach'."); - return NULL; - } - if (!iterator_type) - { - SEMA_ERROR(enumerator, "This type has an iterator without a declared result type, this can't be used with 'foreach'."); - return NULL; - } - assert(iterator_type->resolve_status == RESOLVE_DONE); - Type *it_type = iterator_type->type->canonical; - if (it_type->type_kind != TYPE_STRUCT) - { - SEMA_ERROR(enumerator, "This type has an implementation of 'iterator()' that doesn't return a struct, so it can't be used with 'foreach'."); - return NULL; - } - return method; -} - -static Decl *find_iterator_next(SemaContext *context, Expr *enumerator) -{ - Type *type = enumerator->type->canonical; - assert(type->type_kind == TYPE_STRUCT); - Decl *ambiguous = NULL; - Decl *private = NULL; - Decl *method = sema_resolve_method(context->unit, type->decl, kw_next, &ambiguous, &private); - if (!decl_ok(method)) return NULL; - if (!method) - { - if (ambiguous) - { - SEMA_ERROR(enumerator, "The iterator %s has ambiguous 'next' definitions.", type_quoted_error_string(type)); - return NULL; - } - if (private) - { - SEMA_ERROR(enumerator, - "The iterator %s has a private 'next' definition.", - type_quoted_error_string(type)); - return NULL; - } - SEMA_ERROR(enumerator, - "The iterator %s is missing a definition for 'next()'.", - type_quoted_error_string(type)); - return NULL; - } - Decl **parameters; - TypeInfo *rtype; - switch (method->decl_kind) - { - case DECL_GENERIC: - parameters = method->generic_decl.parameters; - rtype = method->generic_decl.rtype; - break; - case DECL_MACRO: - parameters = method->macro_decl.parameters; - rtype = method->macro_decl.rtype; - break; - case DECL_FUNC: - parameters = method->func_decl.function_signature.params; - rtype = method->func_decl.function_signature.returntype; - break; - default: - UNREACHABLE - } - if (vec_size(parameters) != 2) - { - SEMA_ERROR(enumerator, "An iterator with a 'next()' that take takes %d parameters can't be used for 'foreach', it should have 1.", vec_size(parameters) - 1); - return NULL; - } - if (!rtype) - { - SEMA_ERROR(enumerator, "This type has an iterator without a declared return type, this can't be used with 'foreach'."); - return NULL; - } - return method; -} - -static Expr *sema_insert_method_macro_call(SemaContext *context, SourceSpan span, Decl *macro_decl, Expr *parent, Expr **arguments) +static Expr *sema_insert_method_macro_call(SemaContext *context, SourceSpan span, Decl *method_decl, Expr *parent, Expr **arguments) { Expr *len_call = expr_new(EXPR_CALL, span); len_call->resolve_status = RESOLVE_RUNNING; - len_call->call_expr.func_ref = macro_decl; + len_call->call_expr.func_ref = method_decl; len_call->call_expr.arguments = arguments; len_call->call_expr.body = NULL; len_call->call_expr.unsplat_last = false; len_call->call_expr.is_type_method = true; - if (!sema_expr_analyse_macro_call(context, len_call, parent, macro_decl, false)) return poisoned_expr; + bool is_macro = method_decl->decl_kind == DECL_MACRO; + if (!is_macro) + { + if (parent->type->type_kind != TYPE_POINTER) expr_insert_addr(parent); + } + if (!sema_expr_analyse_general_call(context, len_call, method_decl, parent, is_macro, false)) return poisoned_expr; len_call->resolve_status = RESOLVE_DONE; return len_call; } @@ -1160,9 +1024,9 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen if (!value_type) { - len = sema_find_operator(context, enumerator, kw_operator_len); - Decl *by_val = sema_find_operator(context, enumerator, kw_operator_element_at); - Decl *by_ref = sema_find_operator(context, enumerator, kw_operator_element_at_ref); + len = sema_find_operator(context, enumerator, OVERLOAD_LEN); + Decl *by_val = sema_find_operator(context, enumerator, OVERLOAD_ELEMENT_AT); + Decl *by_ref = sema_find_operator(context, enumerator, OVERLOAD_ELEMENT_REF); if (!len || (!by_val && !by_ref)) { SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type %s.", type_quoted_error_string(enumerator->type)); diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 6703fb6dc..225c8f3d8 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -45,10 +45,9 @@ const char *kw_elements; const char *kw_errors; const char *kw_inf; const char *kw_inline; -const char *kw_iterator; -const char *kw_operator_element_at; -const char *kw_operator_element_at_ref; -const char *kw_operator_len; +const char *kw_elementat; +const char *kw_elementref; +const char *kw_elementset; const char *kw_len; const char *kw_main; const char *kw_max; @@ -138,10 +137,9 @@ void symtab_init(uint32_t capacity) kw_errors = KW_DEF("errors"); kw_inf = KW_DEF("inf"); kw_inline = KW_DEF("inline"); - kw_iterator = KW_DEF("iterator"); - kw_operator_element_at = KW_DEF("operator_element_at"); - kw_operator_element_at_ref = KW_DEF("operator_element_at_ref"); - kw_operator_len = KW_DEF("operator_len"); + kw_elementat = KW_DEF("elementat"); + kw_elementref = KW_DEF("elementref"); + kw_elementset = KW_DEF("elementset"); kw_len = KW_DEF("len"); kw_main = KW_DEF("main"); kw_max = KW_DEF("max"); @@ -213,6 +211,7 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_REGCALL] = KW_DEF("regcall"); attribute_list[ATTRIBUTE_FASTCALL] = KW_DEF("fastcall"); attribute_list[ATTRIBUTE_OVERLAP] = KW_DEF("overlap"); + attribute_list[ATTRIBUTE_OPERATOR] = KW_DEF("operator"); attribute_list[ATTRIBUTE_AUTOIMPORT] = KW_DEF("autoimport"); for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++) diff --git a/src/version.h b/src/version.h index d2efbf31a..8b8a47f9d 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "PRE.20" \ No newline at end of file +#define COMPILER_VERSION "PRE.21" \ No newline at end of file diff --git a/test/test_suite/statements/custom_foreach_with_ref.c3t b/test/test_suite/statements/custom_foreach_with_ref.c3t index 77013884c..3a32d7c7d 100644 --- a/test/test_suite/statements/custom_foreach_with_ref.c3t +++ b/test/test_suite/statements/custom_foreach_with_ref.c3t @@ -8,17 +8,17 @@ struct Foo extern fn void printf(char*, ...); -macro int* Foo.operator_element_at_ref(Foo &f, int a) +macro int* Foo.operator_element_at_ref(Foo &f, int a) @operator(elementref) { return &f.a[a]; } -macro int Foo.operator_len(Foo &f) +macro int Foo.operator_len(Foo &f) @operator(len) { return 3; } -macro int Foo.operator_element_at(Foo &f, int a) +macro int Foo.operator_element_at(Foo &f, int a) @operator(elementat) { return f.a[a]; } diff --git a/test/test_suite/statements/foreach_custom.c3t b/test/test_suite/statements/foreach_custom.c3t index 42f849563..0e528e5ec 100644 --- a/test/test_suite/statements/foreach_custom.c3t +++ b/test/test_suite/statements/foreach_custom.c3t @@ -6,12 +6,12 @@ struct Foo int[] x; } -macro int Foo.operator_element_at(Foo &foo, usize index) +macro int Foo.operator_element_at(Foo &foo, usize index) @operator(elementat) { return foo.x[index]; } -macro usize Foo.operator_len(Foo &foo) +macro usize Foo.operator_len(Foo &foo) @operator(len) { return foo.x.len; } diff --git a/test/test_suite/statements/foreach_custom_macro.c3t b/test/test_suite/statements/foreach_custom_macro.c3t index 556c1f8fb..112cffd61 100644 --- a/test/test_suite/statements/foreach_custom_macro.c3t +++ b/test/test_suite/statements/foreach_custom_macro.c3t @@ -5,12 +5,12 @@ struct Foo int[] x; } -macro int Foo.operator_element_at(Foo &foo, usize index) +macro int Foo.operator_element_at(Foo &foo, usize index) @operator(elementat) { return foo.x[index]; } -macro usize Foo.operator_len(Foo &foo) +macro usize Foo.operator_len(Foo &foo) @operator(len) { return foo.x.len; }