Skip to content

Commit

Permalink
checker, parser, cgen: allow static call on generic type (#21071)
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp committed Mar 22, 2024
1 parent dbdbfe2 commit 29e5124
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 3 deletions.
17 changes: 17 additions & 0 deletions vlib/v/ast/table.v
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
9 changes: 8 additions & 1 deletion vlib/v/checker/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
4 changes: 2 additions & 2 deletions vlib/v/parser/parser.v
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
63 changes: 63 additions & 0 deletions vlib/v/tests/generic_static_call_test.v
Original file line number Diff line number Diff line change
@@ -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')
}

0 comments on commit 29e5124

Please sign in to comment.