Skip to content

Commit

Permalink
Not possible to alias or take reference for extension methods on non-…
Browse files Browse the repository at this point in the history
…user defined types. #1637
  • Loading branch information
lerno committed Nov 21, 2024
1 parent 22f7faf commit ae1b39e
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 22 deletions.
1 change: 1 addition & 0 deletions releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
59 changes: 40 additions & 19 deletions src/compiler/sema_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion test/test_suite/compile_time_introspection/nameof_err.c3
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 2 additions & 2 deletions test/test_suite/compile_time_introspection/sizeof_errors.c3
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
19 changes: 19 additions & 0 deletions test/test_suite/methods/method_ref_for_extension_method.c3
Original file line number Diff line number Diff line change
@@ -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));
}

0 comments on commit ae1b39e

Please sign in to comment.