From a0a8b7e47a4dd2aa976a36f0e995fec7b95373c0 Mon Sep 17 00:00:00 2001 From: yuyi Date: Mon, 8 May 2023 01:53:56 +0800 Subject: [PATCH] ast, parser, checker, cgen, fmt: implement generic fn params (fix #14937) (#18126) --- vlib/v/ast/ast.v | 17 +++++++++-------- vlib/v/ast/table.v | 2 +- vlib/v/checker/checker.v | 11 ++++++++++- vlib/v/fmt/fmt.v | 11 +++++++++++ vlib/v/gen/c/cgen.v | 2 ++ vlib/v/parser/parser.v | 13 ++++++++++--- vlib/v/tests/generics_fn_variable_test.v | 20 ++++++++++++++++++++ 7 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 vlib/v/tests/generics_fn_variable_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index eb4cbc2745..b4b2b5884a 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -935,14 +935,15 @@ pub: mut_pos token.Pos comptime bool pub mut: - scope &Scope = unsafe { nil } - obj ScopeObject - mod string - name string - kind IdentKind - info IdentInfo - is_mut bool // if mut *token* is before name. Use `is_mut()` to lookup mut variable - or_expr OrExpr + scope &Scope = unsafe { nil } + obj ScopeObject + mod string + name string + kind IdentKind + info IdentInfo + is_mut bool // if mut *token* is before name. Use `is_mut()` to lookup mut variable + or_expr OrExpr + concrete_types []Type } pub fn (i &Ident) is_mut() bool { diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index e6d216b526..219a3a00ba 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -744,7 +744,7 @@ pub fn (mut t Table) register_anon_struct(name string, sym_idx int) { } pub fn (t &Table) known_type(name string) bool { - return t.find_type_idx(name) != 0 || t.parsing_type == name + return t.find_type_idx(name) != 0 || t.parsing_type == name || name in ['i32', 'byte'] } // start_parsing_type open the scope during the parsing of a type diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 2bbbbf3603..3329d59e9f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3387,7 +3387,16 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { } // Non-anon-function object (not a call), e.g. `onclick(my_click)` if func := c.table.find_fn(name) { - fn_type := ast.new_type(c.table.find_or_register_fn_type(func, false, true)) + mut fn_type := ast.new_type(c.table.find_or_register_fn_type(func, false, + true)) + if func.generic_names.len > 0 { + if typ_ := c.table.resolve_generic_to_concrete(fn_type, func.generic_names, + node.concrete_types) + { + fn_type = typ_ + c.table.register_fn_concrete_types(func.fkey(), node.concrete_types) + } + } node.name = name node.kind = .function node.info = ast.IdentFn{ diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index e694985f30..875a7c739b 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -2018,6 +2018,17 @@ pub fn (mut f Fmt) ident(node ast.Ident) { } name := f.short_module(node.name) f.write(name) + if node.concrete_types.len > 0 { + f.write('[') + for i, concrete_type in node.concrete_types { + typ_name := f.table.type_to_str_using_aliases(concrete_type, f.mod2alias) + f.write(typ_name) + if i != node.concrete_types.len - 1 { + f.write(', ') + } + } + f.write(']') + } if node.or_expr.kind == .propagate_option { f.write('?') } else if node.or_expr.kind == .block { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 24424e5e14..6c4a92277e 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4215,6 +4215,8 @@ fn (mut g Gen) ident(node ast.Ident) { if cattr := func.attrs.find_first('c') { name = cattr.arg } + } else if node.concrete_types.len > 0 { + name = g.generic_fn_name(node.concrete_types, name) } } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 246d7230b6..b67bf4f882 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -2116,7 +2116,6 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt { } pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident { - // p.warn('name ') is_option := p.tok.kind == .question && p.peek_tok.kind == .lsbr if is_option { p.next() @@ -2169,9 +2168,13 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident { scope: p.scope } } - if p.inside_match_body && name == 'it' { - // p.warn('it') + mut is_known_generic_fn := false + if func := p.table.find_fn(p.prepend_mod(name)) { + if func.generic_names.len > 0 { + is_known_generic_fn = true + } } + mut concrete_types := []ast.Type{} if p.expr_mod.len > 0 { name = '${p.expr_mod}.${name}' } @@ -2188,6 +2191,9 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident { } else if allowed_cases && p.tok.kind == .key_orelse { or_kind = ast.OrKind.block or_stmts, or_pos = p.or_block(.no_err_var) + } else if is_known_generic_fn && p.tok.kind == .lsbr { + // `generic_fn[int]` + concrete_types = p.parse_concrete_types() } return ast.Ident{ tok_kind: p.tok.kind @@ -2212,6 +2218,7 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident { stmts: or_stmts pos: or_pos } + concrete_types: concrete_types } } diff --git a/vlib/v/tests/generics_fn_variable_test.v b/vlib/v/tests/generics_fn_variable_test.v new file mode 100644 index 0000000000..a61ee3bff9 --- /dev/null +++ b/vlib/v/tests/generics_fn_variable_test.v @@ -0,0 +1,20 @@ +type Fn[T] = fn (x T, i int) T + +fn func[T](x T, i int, f_ Fn[T]) T { + return f_(x, i) +} + +fn f[T](x T, i int) T { + return x +} + +fn test_generic_fn_variable() { + ff := f[int] + ret := ff(1, 11) + println(ret) + assert ret == 1 + + x := func[f64](2.0, 3, f[f64]) + println(x) + assert x == 2.0 +}