diff --git a/releasenotes.md b/releasenotes.md index 6913931bd..63ed30947 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -19,6 +19,7 @@ - Fix issue with properties in different targets not being respected #1633. - Indexing an Optional slice would crash in codegen #1636. - SimpleHeapAllocator bug when splitting blocks allowed memory overrun. +- Not possible to alias or take reference for extension methods on non-user defined types. #1637 ### Stdlib changes - Add `io::MultiReader`, `io::MultiWriter`, and `io::TeeReader` structs. diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 8ed478189..6eaab8e73 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3641,6 +3641,34 @@ 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) +{ + ASSERT0(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_IDENTIFIER); @@ -3659,9 +3687,16 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp if (!type_may_have_sub_elements(canonical)) { - if (missing_ref) goto MISSING_REF; - SEMA_ERROR(expr, "'%s' does not have a property '%s'.", type_to_error_string(parent_type), name); - return false; + Decl *member = sema_check_for_type_method(context, expr, parent_type->canonical, name, missing_ref); + if (!decl_ok(member)) return false; + if (!member) + { + if (missing_ref) goto MISSING_REF; + RETURN_SEMA_ERROR(expr, "'%s' does not have a property or method '%s'.", type_to_error_string(parent_type), name); + } + expr->expr_kind = EXPR_IDENTIFIER; + expr_resolve_ident(expr, member); + return true; } Decl *decl = canonical->decl; if (!decl_ok(decl)) return false; @@ -3704,26 +3739,12 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp UNREACHABLE } - Decl *member = sema_decl_stack_find_decl_member(context, decl, name); if (!decl_ok(member)) return false; if (!member) { - Decl *ambiguous = NULL; - Decl *private = NULL; - member = sema_resolve_type_method(context->unit, decl->type, name, &ambiguous, &private); - if (private) - { - if (missing_ref) goto MISSING_REF; - SEMA_ERROR(expr, "The method '%s' has private visibility.", name); - return false; - } - 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'", - name, member->unit->module->name->module, ambiguous->unit->module->name->module); - } + member = sema_check_for_type_method(context, expr, decl->type, name, missing_ref); + if (!decl_ok(member)) return false; } if (!member) { diff --git a/test/test_suite/compile_time_introspection/nameof_err.c3 b/test/test_suite/compile_time_introspection/nameof_err.c3 index 0de24c9ee..f04049945 100644 --- a/test/test_suite/compile_time_introspection/nameof_err.c3 +++ b/test/test_suite/compile_time_introspection/nameof_err.c3 @@ -11,7 +11,7 @@ fn void main2() fn void main3() { - $evaltype("int").extnameof; // #error: 'int' does not have a property 'extnameof' + $evaltype("int").extnameof; // #error: 'int' does not have a property or method 'extnameof' } fn void main4() diff --git a/test/test_suite/compile_time_introspection/sizeof_errors.c3 b/test/test_suite/compile_time_introspection/sizeof_errors.c3 index 1496dc056..b07e18623 100644 --- a/test/test_suite/compile_time_introspection/sizeof_errors.c3 +++ b/test/test_suite/compile_time_introspection/sizeof_errors.c3 @@ -29,12 +29,12 @@ fn void g() fn void k() { - int v = $evaltype("int").x.sizeof; // #error: 'int' does not have a property 'x'. + int v = $evaltype("int").x.sizeof; // #error: 'x'. } fn void l() { - int v = $sizeof(int[].len); // #error: 'int[]' does not have a property 'len' + int v = $sizeof(int[].len); // #error: 'len' } fn void m() diff --git a/test/test_suite/methods/method_ref_for_extension_method.c3 b/test/test_suite/methods/method_ref_for_extension_method.c3 new file mode 100644 index 000000000..57d31a636 --- /dev/null +++ b/test/test_suite/methods/method_ref_for_extension_method.c3 @@ -0,0 +1,19 @@ +module testc3; +import std::io; + +struct Foo { + int i; +} + +fn bool Foo[].is_empty(Foo[] array) { + return array.len == 0; +} + +def foo_arr_is_empty = Foo[].is_empty; + +fn void main() { + Foo[] foos = { Foo { .i = 0 } }; + void* foo = &Foo[].is_empty; + io::printfn("Is empty: %s", foos.is_empty()); + io::printfn("Is empty: %s", foo_arr_is_empty(foos)); +}