From 4179e1cac206e085ca3e53331989d2339a4256ab Mon Sep 17 00:00:00 2001 From: spaceface Date: Mon, 16 May 2022 11:05:08 +0200 Subject: [PATCH] checker: allow using methods as vars when expecting a ctx arg (#14414) --- vlib/v/ast/ast.v | 15 ++++++++------- vlib/v/checker/checker.v | 9 +++++++++ vlib/v/gen/c/cgen.v | 4 ++++ vlib/v/tests/methods_as_fields_test.v | 22 ++++++++++++++++++++++ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 6c52ca292c..1d95b45fc3 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -256,13 +256,14 @@ pub: mut_pos token.Pos next_token token.Kind pub mut: - expr Expr // expr.field_name - expr_type Type // type of `Foo` in `Foo.bar` - typ Type // type of the entire thing (`Foo.bar`) - name_type Type // T in `T.name` or typeof in `typeof(expr).name` - gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown - scope &Scope - from_embed_types []Type // holds the type of the embed that the method is called from + expr Expr // expr.field_name + expr_type Type // type of `Foo` in `Foo.bar` + typ Type // type of the entire thing (`Foo.bar`) + name_type Type // T in `T.name` or typeof in `typeof(expr).name` + gkind_field GenericKindField // `T.name` => ast.GenericKindField.name, `T.typ` => ast.GenericKindField.typ, or .unknown + scope &Scope + from_embed_types []Type // holds the type of the embed that the method is called from + has_hidden_receiver bool } // root_ident returns the origin ident where the selector started. diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index a7dbbb1775..99e456eca3 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1851,6 +1851,14 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { return field.typ } if mut method := c.table.find_method(sym, field_name) { + if c.expected_type != 0 && c.expected_type != ast.none_type { + fn_type := ast.new_type(c.table.find_or_register_fn_type(c.mod, method, false, + true)) + // if the expected type includes the receiver, don't hide it behind a closure + if c.check_types(fn_type, c.expected_type) { + return fn_type + } + } receiver := method.params[0].typ if receiver.nr_muls() > 0 { if !c.inside_unsafe { @@ -1867,6 +1875,7 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { } } method.params = method.params[1..] + node.has_hidden_receiver = true fn_type := ast.new_type(c.table.find_or_register_fn_type(c.mod, method, false, true)) return fn_type diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 234bc69e26..273127ce44 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -3347,6 +3347,10 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } } if !has_embeds { + if !node.has_hidden_receiver { + g.write('${g.typ(node.expr_type.idx())}_$m.name') + return + } receiver := m.params[0] expr_styp := g.typ(node.expr_type.idx()) data_styp := g.typ(receiver.typ.idx()) diff --git a/vlib/v/tests/methods_as_fields_test.v b/vlib/v/tests/methods_as_fields_test.v index 7676825f6e..964aaf4e84 100644 --- a/vlib/v/tests/methods_as_fields_test.v +++ b/vlib/v/tests/methods_as_fields_test.v @@ -95,3 +95,25 @@ fn test_methods_as_fields_ref() { new := set_val(5) assert old == new && old == 1 } + +struct GG_Ctx { + frame_fn fn (voidptr) int +} + +[heap] +struct App { + msg string = 'hello' +} + +fn (app &App) frame() int { + return app.msg.len +} + +fn test_ctx_arg_expected() { + mut app := &App{} + mut ctx := &GG_Ctx{ + frame_fn: app.frame + } + assert typeof(ctx.frame_fn).str() == 'fn (voidptr) int' + assert ctx.frame_fn(app) == 5 +}