From bd2b216ac74ba4b73c3e1c51ed1d959cc3d8de88 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Tue, 3 Jan 2023 05:14:23 -0300 Subject: [PATCH] cgen,checker: fix method call with comptime var (#16844) --- vlib/v/checker/fn.v | 14 +++++++-- vlib/v/gen/c/fn.v | 21 +++++++++++-- vlib/v/tests/method_call_var_comp_test.v | 39 ++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 vlib/v/tests/method_call_var_comp_test.v diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index f17743c085..dcc1073bb5 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1578,9 +1578,17 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { mut got_arg_typ := c.check_expr_opt_call(arg.expr, c.expr(arg.expr)) node.args[i].typ = got_arg_typ if c.inside_comptime_for_field && method.params[param_idx].typ.has_flag(.generic) { - c.table.register_fn_concrete_types(method.fkey(), [ - c.comptime_fields_default_type, - ]) + arg_sym := c.table.sym(c.comptime_fields_default_type) + if arg_sym.kind == .array + && c.table.sym(method.params[param_idx].typ).kind == .array { + c.table.register_fn_concrete_types(method.fkey(), [ + (arg_sym.info as ast.Array).elem_type, + ]) + } else { + c.table.register_fn_concrete_types(method.fkey(), [ + c.comptime_fields_default_type, + ]) + } } else if c.inside_for_in_any_cond && method.params[param_idx].typ.has_flag(.generic) { c.table.register_fn_concrete_types(method.fkey(), [ c.for_in_any_val_type, diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index a8e2a8157c..cf4d32b11f 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -769,6 +769,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) { mut unwrapped_rec_type := node.receiver_type mut has_comptime_field := false mut for_in_any_var_type := ast.void_type + mut comptime_args := []int{} if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0 { // in generic fn unwrapped_rec_type = g.unwrap_generic(node.receiver_type) } else { // in non-generic fn @@ -795,10 +796,12 @@ fn (mut g Gen) method_call(node ast.CallExpr) { node_.args[i].typ = call_arg.expr.obj.typ if call_arg.expr.obj.is_comptime_field { has_comptime_field = true + comptime_args << i } } } else if mut call_arg.expr is ast.ComptimeSelector { has_comptime_field = true + comptime_args << i } } } @@ -1130,7 +1133,20 @@ fn (mut g Gen) method_call(node ast.CallExpr) { } if g.comptime_for_field_type != 0 && g.inside_comptime_for_field && has_comptime_field { - name = g.generic_fn_name([g.comptime_for_field_type], name) + mut concrete_types := node.concrete_types.map(g.unwrap_generic(it)) + arg_sym := g.table.sym(g.comptime_for_field_type) + if m := g.table.find_method(g.table.sym(node.left_type), node.name) { + for k in comptime_args { + if m.generic_names.len > 0 && arg_sym.kind == .array + && m.params[k + 1].typ.has_flag(.generic) + && g.table.final_sym(m.params[k + 1].typ).kind == .array { + concrete_types[k] = (arg_sym.info as ast.Array).elem_type + } else { + concrete_types[k] = g.comptime_for_field_type + } + } + } + name = g.generic_fn_name(concrete_types, name) } else if g.inside_for_in_any_cond && for_in_any_var_type != ast.void_type { name = g.generic_fn_name([for_in_any_var_type], name) } else { @@ -1391,7 +1407,8 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { mut concrete_types := node.concrete_types.map(g.unwrap_generic(it)) arg_sym := g.table.sym(g.comptime_for_field_type) for k in comptime_args { - if arg_sym.kind == .array { + if arg_sym.kind == .array && func.params[k].typ.has_flag(.generic) + && g.table.sym(func.params[k].typ).kind == .array { concrete_types[k] = (arg_sym.info as ast.Array).elem_type } else { concrete_types[k] = g.comptime_for_field_type diff --git a/vlib/v/tests/method_call_var_comp_test.v b/vlib/v/tests/method_call_var_comp_test.v new file mode 100644 index 0000000000..31854c488b --- /dev/null +++ b/vlib/v/tests/method_call_var_comp_test.v @@ -0,0 +1,39 @@ +struct EmptyStruct { + a int + b []int +} + +struct Encoder { +} + +fn (e &Encoder) encode_array[T](val []T) []T { + return val +} + +fn encode_struct[T](val T) []string { + e := Encoder{} + mut out := []string{} + $if T is $Struct { + $for field in T.fields { + out << field.name + out << field.is_array.str() + + $if field.is_array { + value := val.$(field.name) + out << e.encode_array(value).str() + out << e.encode_array([1, 2]).str() + } + } + } + return out +} + +fn test_main() { + out := encode_struct(EmptyStruct{3, [2, 1]}) + assert out[0] == 'a' + assert out[1] == 'false' + assert out[2] == 'b' + assert out[3] == 'true' + assert out[4] == '[2, 1]' + assert out[5] == '[1, 2]' +}