diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 15ca770adc8d3c..33b9d3e5b6c978 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1544,6 +1544,23 @@ pub fn (t Table) does_type_implement_interface(typ Type, inter_typ Type) bool { return false } +pub fn (mut t Table) resolve_generic_static_type_name(fn_name string, generic_names []string, concrete_types []Type) string { + if index := fn_name.index('__static__') { + if index > 0 { + generic_name := fn_name[0..index] + valid_generic := util.is_generic_type_name(generic_name) + && generic_name in generic_names + if valid_generic { + name_type := Type(t.find_type_idx(generic_name)).set_flag(.generic) + if typ := t.resolve_generic_to_concrete(name_type, generic_names, concrete_types) { + return '${t.type_to_str(typ)}${fn_name[index..]}' + } + } + } + } + return fn_name +} + // resolve_generic_to_concrete resolves generics to real types T => int. // Even map[string]map[string]T can be resolved. // This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl. diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 9f549234817c10..8c54d350923644 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -676,7 +676,14 @@ fn (mut c Checker) needs_unwrap_generic_type(typ ast.Type) bool { } fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.Type { - fn_name := node.name + mut fn_name := node.name + if index := node.name.index('__static__') { + // resolve static call T.name() + if index > 0 && c.table.cur_fn != unsafe { nil } { + fn_name = c.table.resolve_generic_static_type_name(fn_name, c.table.cur_fn.generic_names, + c.table.cur_concrete_types) + } + } if fn_name == 'main' { c.error('the `main` function cannot be called in the program', node.pos) } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 771be7b147f9ca..3b719626bb6813 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1783,6 +1783,13 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { is_selector_call = true } mut name := node.name + if index := node.name.index('__static__') { + // resolve static call T.name() + if index > 0 && g.cur_fn != unsafe { nil } { + name = g.table.resolve_generic_static_type_name(node.name, g.cur_fn.generic_names, + g.cur_concrete_types) + } + } is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic'] print_method := name is_json_encode := name == 'json.encode' diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 3a1786fbfd8622..2ad2d0f74d70aa 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2889,8 +2889,8 @@ fn (mut p Parser) name_expr() ast.Expr { // `if a == Foo{} {...}` or `match foo { Foo{} {...} }` return p.struct_init(p.mod + '.' + p.tok.lit, .normal, is_option) } else if p.peek_tok.kind == .dot && lit0_is_capital && !known_var && language == .v { - // T.name - if p.is_generic_name() { + // T.name selector + if p.is_generic_name() && p.peek_token(3).kind != .lpar { pos := p.tok.pos() name := p.check_name() p.check(.dot) diff --git a/vlib/v/tests/generic_static_call_test.v b/vlib/v/tests/generic_static_call_test.v new file mode 100644 index 00000000000000..8c9d108ede8f0a --- /dev/null +++ b/vlib/v/tests/generic_static_call_test.v @@ -0,0 +1,63 @@ +module main + +type Tag = int +type Octet = string + +fn (o Octet) tag() Tag { + return Tag(1) +} + +fn (o Octet) pack() ![]u8 { + return o.bytes() +} + +fn (o Octet) bytes() []u8 { + s := string(o) + return s.bytes() +} + +// static method on concrete type +fn Octet.unpack(b []u8) Octet { + return Octet(b.bytestr()) +} + +// This is generic type +struct Elm[T] { +mut: + val T +} + +fn Elm.new[T](val T) Elm[T] { + return Elm[T]{ + val: val + } +} + +fn (el Elm[T]) the_t() T { + return el.val +} + +fn Elm.unpack[T](src []u8) !Elm[T] { + t := T.unpack(src) + return Elm[T]{t} +} + +fn (el Elm[T]) tag() Tag { + return el.val.tag() +} + +fn (el Elm[T]) pack() ![]u8 { + return el.val.pack()! +} + +fn test_main() { + b := Octet('xx') + mut el := Elm.new[Octet](b) + bytes := el.pack()! + assert bytes == [u8(120), 120] + + // unpack + ab := Elm.unpack[Octet](bytes)! + assert el == ab + assert el.the_t() == Octet('xx') +}