From 9b28a7aa96e2c8e9e7cd2347b0cd2a35d2a2389f Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Mon, 26 Dec 2022 13:44:18 -0300 Subject: [PATCH] checker,cgen: fix comptime value recognition on generic arguments (#16768) --- vlib/v/ast/ast.v | 1 + vlib/v/checker/check_types.v | 18 +++++++++++ vlib/v/checker/checker.v | 1 + vlib/v/checker/comptime.v | 2 ++ vlib/v/gen/c/assign.v | 9 ++++-- vlib/v/gen/c/fn.v | 9 +++++- vlib/v/tests/comptime_on_generics_func_test.v | 24 ++++++++++++++ .../comptime_var_on_multiple_args_test.v | 31 +++++++++++++++++++ 8 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 vlib/v/tests/comptime_on_generics_func_test.v create mode 100644 vlib/v/tests/comptime_var_on_multiple_args_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 08dee21cb4..ef9a3fe35a 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -590,6 +590,7 @@ pub mut: is_ctor_new bool // if JS ctor calls requires `new` before call, marked as `[use_new]` in V args []CallArg expected_arg_types []Type + comptime_ret_val bool language Language or_block OrExpr left Expr // `user` in `user.register()` diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index e9b0ff7c7f..85f75c82bd 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -871,7 +871,25 @@ fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr) { func_.name = '' idx := c.table.find_or_register_fn_type(func_, true, false) typ = ast.new_type(idx).derive(arg.typ) + } else if c.inside_comptime_for_field && sym.info is ast.Struct + && arg.expr is ast.ComptimeSelector { + compselector := arg.expr as ast.ComptimeSelector + if compselector.field_expr is ast.SelectorExpr { + selectorexpr := compselector.field_expr as ast.SelectorExpr + if selectorexpr.expr is ast.Ident { + ident := selectorexpr.expr as ast.Ident + if ident.name == c.comptime_for_field_var { + typ = c.comptime_fields_default_type + + if func.return_type.has_flag(.generic) + && gt_name == c.table.type_to_str(func.return_type) { + node.comptime_ret_val = true + } + } + } + } } + if arg.expr.is_auto_deref_var() { typ = typ.deref() } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 711587025d..0a397f140a 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -95,6 +95,7 @@ mut: vweb_gen_types []ast.Type // vweb route checks timers &util.Timers = util.get_timers() for_in_any_val_type ast.Type + comptime_for_field_var string comptime_fields_default_type ast.Type comptime_fields_type map[string]ast.Type fn_scope &ast.Scope = unsafe { nil } diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 2b4d9a3349..8654ade577 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -180,6 +180,7 @@ fn (mut c Checker) comptime_for(node ast.ComptimeFor) { sym_info := sym.info as ast.Struct c.inside_comptime_for_field = true for field in sym_info.fields { + c.comptime_for_field_var = node.val_var c.comptime_fields_type[node.val_var] = node.typ c.comptime_fields_default_type = field.typ c.stmts(node.stmts) @@ -188,6 +189,7 @@ fn (mut c Checker) comptime_for(node ast.ComptimeFor) { tsym := c.table.sym(unwrapped_expr_type) c.table.dumps[int(unwrapped_expr_type.clear_flag(.optional).clear_flag(.result))] = tsym.cname } + c.comptime_for_field_var = '' c.inside_comptime_for_field = false } } else { diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 7e87f2d9ee..5bca367df7 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -136,7 +136,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { is_auto_heap = left.obj.is_auto_heap } } - styp := g.typ(var_type) + mut styp := g.typ(var_type) mut is_fixed_array_init := false mut has_val := false match val { @@ -146,7 +146,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { } ast.CallExpr { is_call = true - return_type = val.return_type + if val.comptime_ret_val { + return_type = g.comptime_for_field_type + styp = g.typ(return_type) + } else { + return_type = val.return_type + } } // TODO: no buffer fiddling ast.AnonFn { diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 4850eb70d6..58d5364219 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1234,6 +1234,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { mut is_interface_call := false mut is_selector_call := false mut has_comptime_field := false + mut comptime_args := []int{} if node.left_type != 0 { left_sym := g.table.sym(node.left_type) if left_sym.kind == .interface_ { @@ -1266,10 +1267,12 @@ fn (mut g Gen) fn_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 } } } @@ -1362,7 +1365,11 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { if func.generic_names.len > 0 { 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)) + for k in comptime_args { + concrete_types[k] = g.comptime_for_field_type + } + name = g.generic_fn_name(concrete_types, name) } else { concrete_types := node.concrete_types.map(g.unwrap_generic(it)) name = g.generic_fn_name(concrete_types, name) diff --git a/vlib/v/tests/comptime_on_generics_func_test.v b/vlib/v/tests/comptime_on_generics_func_test.v new file mode 100644 index 0000000000..4996d151af --- /dev/null +++ b/vlib/v/tests/comptime_on_generics_func_test.v @@ -0,0 +1,24 @@ +module main + +struct MyStruct { + b string + c bool +} + +fn get_type[T](t T) T { + return t +} + +fn test_main() { + my := MyStruct{'cool', false} + $for field2 in MyStruct.fields { + $if field2.typ is string { + var := get_type(my.$(field2.name)) + assert dump(var) == 'cool' + } + $if field2.typ is bool { + var := get_type(my.$(field2.name)) + assert dump(var).str() == 'false' + } + } +} diff --git a/vlib/v/tests/comptime_var_on_multiple_args_test.v b/vlib/v/tests/comptime_var_on_multiple_args_test.v new file mode 100644 index 0000000000..45071cae4e --- /dev/null +++ b/vlib/v/tests/comptime_var_on_multiple_args_test.v @@ -0,0 +1,31 @@ +module main + +fn ok3[T](o T) T { + return o +} + +fn ok2[T](o T, s string) string { + return s +} + +fn ok[T](o T, s string) string { + return '${o}${s}' +} + +struct Test { + s string +} + +fn test_main() { + s := Test{'foo'} + + $for f in Test.fields { + assert ok3(s.$(f.name)).str() == 'foo' + } + $for f in Test.fields { + assert ok(s.$(f.name), 'bar') == 'foobar' + } + $for f in Test.fields { + assert ok2(s.$(f.name), 'ok') == 'ok' + } +}