From d564db6ca8a24a125db8ff1684081f1b71fad820 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Wed, 6 Dec 2023 07:21:07 -0300 Subject: [PATCH] checker: fix comptimecall type resolution on checker phase and method.return_type comptime checking and assignment from option comptimecall (#20092) --- vlib/v/checker/checker.v | 7 +- vlib/v/checker/comptime.v | 8 +- vlib/v/checker/if.v | 66 ++++++++++---- .../tests/comptime_call_method_void_err.out | 4 +- vlib/v/gen/c/comptime.v | 4 +- .../v/gen/c/testdata/comptime_option_call.out | 2 +- vlib/v/tests/comptime_method_test.v | 85 +++++++++++++++++++ vlib/v/tests/method_call_resolve_test.v | 31 ++++++- 8 files changed, 180 insertions(+), 27 deletions(-) create mode 100644 vlib/v/tests/comptime_method_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 4261708ba..7d8bfeef2 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -112,9 +112,10 @@ mut: comptime_fields_default_type ast.Type comptime_fields_type map[string]ast.Type comptime_for_field_value ast.StructField // value of the field variable - comptime_enum_field_value string // current enum value name - comptime_for_method string // $for method in T.methods {} - comptime_for_method_var string // $for method in T.methods {}; the variable name + comptime_enum_field_value string // current enum value name + comptime_for_method string // $for method in T.methods {} + comptime_for_method_var string // $for method in T.methods {}; the variable name + comptime_for_method_ret_type ast.Type // $for method - current method.return_type field comptime_values_stack []CurrentComptimeValues // stores the values from the above on each $for loop, to make nesting them easier fn_scope &ast.Scope = unsafe { nil } main_fn_decl_node ast.FnDecl diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 5498ae894..7dac873e8 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -164,8 +164,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { node.args[i].typ = c.expr(mut arg.expr) } c.stmts_ending_with_expression(mut node.or_block.stmts) - // assume string for now - return ast.string_type + return c.get_comptime_var_type(node) } if node.method_name == 'res' { if !c.inside_defer { @@ -344,6 +343,8 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { for method in methods { c.comptime_for_method = method.name c.comptime_for_method_var = node.val_var + c.comptime_for_method_ret_type = method.return_type + c.comptime_fields_type['${node.val_var}.return_type'] = method.return_type c.stmts(mut node.stmts) } c.pop_existing_comptime_values() @@ -1069,6 +1070,7 @@ struct CurrentComptimeValues { comptime_enum_field_value string comptime_for_method string comptime_for_method_var string + comptime_for_method_ret_type ast.Type } fn (mut c Checker) push_existing_comptime_values() { @@ -1081,6 +1083,7 @@ fn (mut c Checker) push_existing_comptime_values() { comptime_enum_field_value: c.comptime_enum_field_value comptime_for_method: c.comptime_for_method comptime_for_method_var: c.comptime_for_method_var + comptime_for_method_ret_type: c.comptime_for_method_ret_type } } @@ -1094,4 +1097,5 @@ fn (mut c Checker) pop_existing_comptime_values() { c.comptime_enum_field_value = old.comptime_enum_field_value c.comptime_for_method = old.comptime_for_method c.comptime_for_method_var = old.comptime_for_method_var + c.comptime_for_method_ret_type = old.comptime_for_method_ret_type } diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index ec412f7ca..e9b36426d 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -155,6 +155,11 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { } else { .skip } + } else if comptime_field_name == c.comptime_for_method_var { + if left.field_name == 'return_type' { + skip_state = c.check_compatible_types(c.unwrap_generic(c.comptime_for_method_ret_type), + right as ast.TypeNode) + } } } else if left is ast.TypeNode { is_comptime_type_is_expr = true @@ -231,27 +236,52 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { } } else if branch.cond.op in [.eq, .ne] && left is ast.SelectorExpr && right is ast.StringLiteral { - if left.expr.str() == c.comptime_for_field_var { - if left.field_name == 'name' { - is_comptime_type_is_expr = true - match branch.cond.op { - .eq { - skip_state = if c.comptime_for_field_value.name == right.val.str() { - ComptimeBranchSkipState.eval - } else { - ComptimeBranchSkipState.skip + match left.expr.str() { + c.comptime_for_field_var { + if left.field_name == 'name' { + is_comptime_type_is_expr = true + match branch.cond.op { + .eq { + skip_state = if c.comptime_for_field_value.name == right.val.str() { + ComptimeBranchSkipState.eval + } else { + ComptimeBranchSkipState.skip + } } + .ne { + skip_state = if c.comptime_for_field_value.name == right.val.str() { + ComptimeBranchSkipState.skip + } else { + ComptimeBranchSkipState.eval + } + } + else {} } - .ne { - skip_state = if c.comptime_for_field_value.name == right.val.str() { - ComptimeBranchSkipState.skip - } else { - ComptimeBranchSkipState.eval + } + } + c.comptime_for_method_var { + if left.field_name == 'name' { + is_comptime_type_is_expr = true + match branch.cond.op { + .eq { + skip_state = if c.comptime_for_method == right.val.str() { + ComptimeBranchSkipState.eval + } else { + ComptimeBranchSkipState.skip + } + } + .ne { + skip_state = if c.comptime_for_method == right.val.str() { + ComptimeBranchSkipState.skip + } else { + ComptimeBranchSkipState.eval + } } + else {} } - else {} } } + else {} } } } @@ -301,7 +331,11 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { node.branches[i].stmts = [] } if comptime_field_name.len > 0 { - c.comptime_fields_type[comptime_field_name] = c.comptime_fields_default_type + if comptime_field_name == c.comptime_for_method_var { + c.comptime_fields_type[comptime_field_name] = c.comptime_for_method_ret_type + } else { + c.comptime_fields_type[comptime_field_name] = c.comptime_fields_default_type + } } c.skip_flags = cur_skip_flags if c.fn_level == 0 && c.pref.output_cross_c && c.ct_cond_stack.len > 0 { diff --git a/vlib/v/checker/tests/comptime_call_method_void_err.out b/vlib/v/checker/tests/comptime_call_method_void_err.out index 1b4fec612..8d10ee051 100644 --- a/vlib/v/checker/tests/comptime_call_method_void_err.out +++ b/vlib/v/checker/tests/comptime_call_method_void_err.out @@ -1,7 +1,7 @@ -vlib/v/checker/tests/comptime_call_method_void_err.vv:17:20: cgen error: method `sample1()` (no value) used as value +vlib/v/checker/tests/comptime_call_method_void_err.vv:17:4: error: `println` can not print void expressions 15 | $for method in Dummy.methods { 16 | if os.args.len >= 1 { 17 | println(Dummy{}.$method(os.args[0])) - | ~~~~~~~~~~~~~~~~~~~ + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 | } 19 | } diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 54c2bbaeb..29eac5634 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -875,9 +875,9 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { styp := g.table.find_type_idx(sig) // TODO: type aliases - ret_typ := method.return_type.idx() + ret_typ := method.return_type g.writeln('\t${node.val_var}.typ = ${styp};') - g.writeln('\t${node.val_var}.return_type = ${ret_typ};') + g.writeln('\t${node.val_var}.return_type = ${ret_typ.idx()};') g.comptime_var_type_map['${node.val_var}.return_type'] = ret_typ g.comptime_var_type_map['${node.val_var}.typ'] = styp diff --git a/vlib/v/gen/c/testdata/comptime_option_call.out b/vlib/v/gen/c/testdata/comptime_option_call.out index 5e83e8918..f9e6dbb56 100644 --- a/vlib/v/gen/c/testdata/comptime_option_call.out +++ b/vlib/v/gen/c/testdata/comptime_option_call.out @@ -6,5 +6,5 @@ Option(none) Option(none) 1 1 -println(NIL) +Option('') Option(none) diff --git a/vlib/v/tests/comptime_method_test.v b/vlib/v/tests/comptime_method_test.v new file mode 100644 index 000000000..b154d046d --- /dev/null +++ b/vlib/v/tests/comptime_method_test.v @@ -0,0 +1,85 @@ +struct Test {} + +struct Test2 {} + +fn (t Test) t_0() ?int { + return none +} + +fn (t Test) t_1() int { + return 0 +} + +fn (t Test) t_2() bool { + return true +} + +fn (t Test) t_3() Test { + return t +} + +fn test_main() { + t := Test{} + assert t_bool(t, 't_2')? == true + assert t_int(t, 't_1')? == 0 + t_struct(t, 't_3') +} + +fn t_bool[T](val T, method_name string) ?bool { + $for f in T.methods { + if method_name == f.name { + $if f.return_type is bool { + return val.$method() + } + $if f.return_type is int { + val.$method() + } + } + } + return none +} + +fn t_int[T](val T, method_name string) ?int { + $for f in T.methods { + if method_name == f.name { + $if f.return_type is bool { + val.$method() + } + $if f.return_type is int { + return val.$method() + } + } + } + return none +} + +fn t_struct[T](val T, method_name string) { + mut s := map[string]string{} + + $for f in T.methods { + $if f.return_type is Test2 { + b := s[f.name] or { + println('z') + '' + } + dump(b) + } + $if f.return_type is Test { + c := val.$method() or { Test{} } + dump(c) + + s[f.name] = f.name + dump(s) + assert f.name == 't_3' + assert s[f.name] == 't_3' + + b := s[f.name] or { + println('z') + '' + } + dump(b) + return + } + } + assert false +} diff --git a/vlib/v/tests/method_call_resolve_test.v b/vlib/v/tests/method_call_resolve_test.v index 7bcf90f1d..af7917a49 100644 --- a/vlib/v/tests/method_call_resolve_test.v +++ b/vlib/v/tests/method_call_resolve_test.v @@ -7,6 +7,8 @@ enum Animal { cat } +struct Encoder {} + type Entity = Animal | Human @[sumtype_to: Animal] @@ -39,7 +41,34 @@ fn encode[T](val T) { } } -fn test_main() { +fn test_func() { encode(Entity(Human{'Monke'})) encode(Entity(Animal.cat)) } + +fn (e &Encoder) encode[T](val T) { + $if T is $sumtype { + $for method in T.methods { + if method.attrs.len >= 1 { + if method.attrs[0].contains('sumtype_to') { + if val.type_name() == method.attrs[0].all_after('sumtype_to:').trim_space() { + println(val.$method()) + e.encode(val.$method()) + } + } + } + } + } $else $if T is $struct { + assert val == Human{ + name: 'Monke' + } + } $else $if T is $enum { + assert val == Animal.cat + } +} + +fn test_method() { + e := Encoder{} + e.encode(Entity(Human{'Monke'})) + e.encode(Entity(Animal.cat)) +} -- 2.39.5