diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index f955637f9b3ce2..1d13cd276373b1 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1630,7 +1630,7 @@ pub fn (t &Table) does_type_implement_interface(typ Type, inter_typ Type) bool { return false } -pub fn (mut t Table) convert_generic_static_type_name(fn_name string, generic_names []string, concrete_types []Type) string { +pub fn (mut t Table) convert_generic_static_type_name(fn_name string, generic_names []string, concrete_types []Type) (Type, string) { if index := fn_name.index('__static__') { if index > 0 { generic_name := fn_name[0..index] @@ -1639,12 +1639,12 @@ pub fn (mut t Table) convert_generic_static_type_name(fn_name string, generic_na if valid_generic { name_type := t.find_type(generic_name).set_flag(.generic) if typ := t.convert_generic_type(name_type, generic_names, concrete_types) { - return '${t.type_to_str(typ)}${fn_name[index..]}' + return name_type, '${t.type_to_str(typ)}${fn_name[index..]}' } } } } - return fn_name + return void_type, fn_name } // convert_generic_type convert generics to real types (T => int) or other generics type. diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index 2aaffc268dfd4d..c5c9d97b4500e6 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -414,14 +414,23 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { if left.obj.ct_type_var in [.generic_var, .no_comptime] && c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len != 0 - && !right.comptime_ret_val - && right.return_type_generic.has_flag(.generic) - && c.is_generic_expr(right) { + && !right.comptime_ret_val && c.is_generic_expr(right) { // mark variable as generic var because its type changes according to fn return generic resolution type left.obj.ct_type_var = .generic_var - fn_ret_type := c.resolve_return_type(right) - if fn_ret_type != ast.void_type - && c.table.final_sym(fn_ret_type).kind != .multi_return { + if right.return_type_generic.has_flag(.generic) { + fn_ret_type := c.resolve_return_type(right) + if fn_ret_type != ast.void_type + && c.table.final_sym(fn_ret_type).kind != .multi_return { + var_type := if right.or_block.kind == .absent { + fn_ret_type + } else { + fn_ret_type.clear_option_and_result() + } + c.comptime.type_map['g.${left.name}.${left.obj.pos.pos}'] = var_type + } + } else if right.is_static_method + && right.left_type.has_flag(.generic) { + fn_ret_type := c.unwrap_generic(c.resolve_return_type(right)) var_type := if right.or_block.kind == .absent { fn_ret_type } else { diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 299af3e1419d03..d73b3c1a8019a2 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -848,8 +848,8 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. if node.is_static_method { // resolve static call T.name() if c.table.cur_fn != unsafe { nil } { - fn_name = c.table.convert_generic_static_type_name(fn_name, c.table.cur_fn.generic_names, - c.table.cur_concrete_types) + node.left_type, fn_name = c.table.convert_generic_static_type_name(fn_name, + c.table.cur_fn.generic_names, c.table.cur_concrete_types) } } if fn_name == 'main' { @@ -1096,6 +1096,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. func = f unsafe { c.table.fns[orig_name].usages++ } node.name = orig_name + node.left_type = typ } } } @@ -1875,6 +1876,9 @@ fn (mut c Checker) is_generic_expr(node ast.Expr) bool { if node.args.any(c.comptime.is_generic_param_var(it.expr)) { return true } + if node.is_static_method && node.left_type.has_flag(.generic) { + return true + } // fn[T]() or generic_var.fn[T]() node.concrete_types.any(it.has_flag(.generic)) } @@ -3916,6 +3920,14 @@ fn (mut c Checker) resolve_return_type(node ast.CallExpr) ast.Type { if method := c.table.find_method(left_sym, node.name) { return c.resolve_fn_return_type(method, node) } + } else if node.is_static_method { + if c.table.cur_fn != unsafe { nil } { + _, name := c.table.convert_generic_static_type_name(node.name, c.table.cur_fn.generic_names, + c.table.cur_concrete_types) + if func := c.table.find_fn(name) { + return c.resolve_fn_return_type(func, node) + } + } } else { if func := c.table.find_fn(node.name) { return c.resolve_fn_return_type(func, node) diff --git a/vlib/v/checker/return.v b/vlib/v/checker/return.v index d000dad20fd1ca..e2353eddcdd729 100644 --- a/vlib/v/checker/return.v +++ b/vlib/v/checker/return.v @@ -107,6 +107,9 @@ fn (mut c Checker) return_stmt(mut node ast.Return) { if expr.obj.smartcasts.len > 0 { typ = c.unwrap_generic(expr.obj.smartcasts.last()) } + if expr.obj.ct_type_var != .no_comptime { + typ = c.comptime.get_type_or_default(expr, typ) + } } } got_types << typ diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 05997157d55b17..a9b32eef5de567 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -322,6 +322,13 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { g.comptime.type_map['g.${left.name}.${left.obj.pos.pos}'] = var_type // eprintln('>> ${func.name} > resolve ${left.name}.${left.obj.pos.pos}.generic to ${g.table.type_to_str(var_type)}') } + } else if val.is_static_method && val.left_type.has_flag(.generic) { + fn_ret_type := g.resolve_return_type(val) + var_type = fn_ret_type + val_type = var_type + left.obj.typ = var_type + g.comptime.type_map['g.${left.name}.${left.obj.pos.pos}'] = var_type + g.assign_ct_type = var_type } } is_auto_heap = left.obj.is_auto_heap diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index ee815f50822aa4..6616a8f6cd6989 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1386,6 +1386,23 @@ fn (mut g Gen) resolve_return_type(node ast.CallExpr) ast.Type { } } } + } else if node.is_static_method { + if g.cur_fn != unsafe { nil } { + _, name := g.table.convert_generic_static_type_name(node.name, g.cur_fn.generic_names, + g.cur_concrete_types) + if func := g.table.find_fn(name) { + return if node.or_block.kind == .absent { + func.return_type + } else { + func.return_type.clear_option_and_result() + } + } + } + return if node.or_block.kind == .absent { + node.return_type + } else { + node.return_type.clear_option_and_result() + } } else { if func := g.table.find_fn(node.name) { if func.generic_names.len > 0 { @@ -2014,7 +2031,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { // will be `0` for `foo()` mut is_interface_call := false mut is_selector_call := false - if node.left_type != 0 { + if node.is_method && node.left_type != 0 { mut fn_typ := ast.no_type left_sym := g.table.sym(node.left_type) if node.is_field { @@ -2048,7 +2065,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { if node.is_static_method { // resolve static call T.name() if g.cur_fn != unsafe { nil } { - name = g.table.convert_generic_static_type_name(node.name, g.cur_fn.generic_names, + _, name = g.table.convert_generic_static_type_name(node.name, g.cur_fn.generic_names, g.cur_concrete_types) } } @@ -2276,7 +2293,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { // Temp fix generate call fn error when the struct type of sumtype // has the fn field and is same to the struct name. mut is_cast_needed := true - if node.left_type != 0 { + if node.is_method && node.left_type != 0 { left_sym := g.table.sym(node.left_type) if left_sym.kind == .struct && node.name == obj.name { is_cast_needed = false diff --git a/vlib/v/tests/fns/generic_static_method_call_test.v b/vlib/v/tests/fns/generic_static_method_call_test.v new file mode 100644 index 00000000000000..a71acbf32052df --- /dev/null +++ b/vlib/v/tests/fns/generic_static_method_call_test.v @@ -0,0 +1,48 @@ +struct Parser { +mut: + data []u8 +} + +fn (mut p Parser) read_element[T]() !T { + t := T.parse(mut p)! + return t +} + +struct TestA { + data []u8 +} + +fn TestA.parse(mut p Parser) !TestA { + return TestA{ + data: p.data + } +} + +struct TestB { + st string +} + +fn TestB.parse(mut p Parser) !TestB { + return TestB{ + st: p.data.hex() + } +} + +fn test_main() { + data := []u8{len: 5, init: u8(5)} + mut p := Parser{ + data: data + } + + ta := p.read_element[TestA]()! + dump(ta) + assert ta == TestA{ + data: [u8(5), 5, 5, 5, 5] + } + + tb := p.read_element[TestB]()! + dump(tb) + assert tb == TestB{ + st: '0505050505' + } +}