From 9a31744255057495d7c46bc332a027fab918eca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Wed, 30 Dec 2020 23:49:02 +0100 Subject: [PATCH] cgen: fix embed generic field access & method call (#7725) --- vlib/v/checker/checker.v | 7 +++++-- vlib/v/gen/cgen.v | 2 +- vlib/v/tests/struct_embed_test.v | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 9257b0939e..8418195ef4 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1258,6 +1258,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { } mut method := table.Fn{} mut has_method := false + mut is_method_from_embed := false if m := c.table.type_find_method(left_type_sym, method_name) { method = m has_method = true @@ -1275,6 +1276,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { if found_methods.len == 1 { method = found_methods[0] has_method = true + is_method_from_embed = true call_expr.from_embed_type = embed_of_found_methods[0] } else if found_methods.len > 1 { c.error('ambiguous method `$method_name`', call_expr.pos) @@ -1374,12 +1376,13 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { call_expr.expected_arg_types << method.params[i].typ } } - if is_generic { + if is_method_from_embed { + call_expr.receiver_type = call_expr.from_embed_type.derive(method.params[0].typ) + } else if is_generic { // We need the receiver to be T in cgen. // TODO: cant we just set all these to the concrete type in checker? then no need in gen call_expr.receiver_type = left_type.derive(method.params[0].typ).set_flag(.generic) } else { - // note: correct receiver type is automatically set here on struct embed calls call_expr.receiver_type = method.params[0].typ } call_expr.return_type = method.return_type diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 892fd531c2..dd7e6573dc 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2768,7 +2768,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { if node.expr_type == 0 { g.checker_bug('unexpected SelectorExpr.expr_type = 0', node.pos) } - sym := g.table.get_type_symbol(node.expr_type) + sym := g.table.get_type_symbol(g.unwrap_generic(node.expr_type)) // if node expr is a root ident and an optional mut is_optional := node.expr is ast.Ident && node.expr_type.has_flag(.optional) if is_optional { diff --git a/vlib/v/tests/struct_embed_test.v b/vlib/v/tests/struct_embed_test.v index ac0165165f..44892d4bb5 100644 --- a/vlib/v/tests/struct_embed_test.v +++ b/vlib/v/tests/struct_embed_test.v @@ -116,3 +116,35 @@ fn test_embed_mutable() { mut a := field_publicity.App{} a.Context = field_publicity.Context{} } + +struct Context { + static_files string +} + +fn (c Context) test() bool { + return true +} + +struct App { + Context +} + +fn embed_field_access_generic(mut app T) { + app.Context = Context{ + static_files: app.static_files + } +} + +fn test_embed_field_access_generic() { + mut app := App{} + embed_field_access_generic(mut app) +} + +fn embed_method_generic(app T) bool { + return app.test() +} + +fn test_embed_method_generic() { + mut app := App{} + assert embed_method_generic(app) +}