From 280c7d396c28c9ad5074c08bb521d7dfb021cf58 Mon Sep 17 00:00:00 2001 From: Simon Heuser <39399016+shsr04@users.noreply.github.com> Date: Fri, 25 Oct 2019 20:32:27 +0200 Subject: [PATCH] generics: generic methods, cast to T --- vlib/compiler/fn.v | 43 +++++++++++++++++++++++------- vlib/compiler/gen_c.v | 11 +++----- vlib/compiler/parser.v | 5 +++- vlib/compiler/tests/generic_test.v | 42 ++++++++++++++++++++++------- 4 files changed, 75 insertions(+), 26 deletions(-) diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index 4127dc3953..a4eb69a412 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -392,8 +392,23 @@ fn (p mut Parser) fn_decl() { if f.is_generic { if p.first_pass() { f.body_idx = p.cur_tok_index()+1 - p.table.register_fn(f) + if f.is_method { + rcv := p.table.find_type(receiver_typ) + if p.first_pass() && rcv.name == '' { + r := Type { + name: rcv.name.replace('*', '') + mod: p.mod + is_placeholder: true + } + p.table.register_type2(r) + } + // println('added generic method $rcv.name $f.name') + p.add_method(rcv.name, f) + } else { + p.table.register_fn(f) + } } + if f.is_method { p.mark_var_changed(f.args[0]) } p.check_unused_variables() p.set_current_fn( EmptyFn ) p.returns = false @@ -692,9 +707,6 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s // If we have a method placeholder, // we need to preappend "method(receiver, ...)" if f.is_method { - if f.is_generic { - p.error('generic methods are not yet implemented') - } receiver := f.args.first() //println('r=$receiver.typ RT=$receiver_type') if receiver.is_mut && !p.expr_var.is_mut { @@ -709,8 +721,8 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s if !p.expr_var.is_changed { p.mark_var_changed(p.expr_var) } - met_name := if f.is_generic { f.name } else { cgen_name } - p.gen_method_call(receiver_type, f.typ, met_name, receiver, method_ph) + met_call := p.gen_method_call(receiver, receiver_type, cgen_name, f.typ) + p.cgen.set_placeholder(method_ph, met_call) } else { // Normal function call p.gen('$cgen_name (') @@ -1082,8 +1094,8 @@ fn (p mut Parser) extract_type_inst(f &Fn, args_ []string) TypeInst { mut r := TypeInst{} mut i := 0 mut args := args_ - args << f.typ - for ai, e in args { + if f.typ != 'void' { args << f.typ } + for e in args { if e == '' { continue } tp := f.type_pars[i] mut ti := e @@ -1118,6 +1130,11 @@ fn (p mut Parser) extract_type_inst(f &Fn, args_ []string) TypeInst { if r.inst[f.typ] == '' && f.typ in f.type_pars { r.inst[f.typ] = '_ANYTYPE_' } + for tp in f.type_pars { + if r.inst[tp] == '' { + p.error_with_token_index('unused type parameter `$tp`', f.body_idx-2) + } + } return r } @@ -1257,6 +1274,9 @@ fn (p mut Parser) register_multi_return_stuct(types []string) string { } fn (p mut Parser) rename_generic_fn_instance(f mut Fn, ti TypeInst) { + if f.is_method { + f.name = f.receiver_typ + '_' + f.name + } f.name = f.name + '_T' for k in ti.inst.keys() { f.name = f.name + '_' + type_to_safe_str(ti.inst[k].replace('...', '')) @@ -1326,7 +1346,12 @@ fn (p mut Parser) dispatch_generic_fn_instance(f mut Fn, ti TypeInst) { if f.typ in ti.inst { f.typ = ti.inst[f.typ] } - p.table.register_fn(f) + + if f.is_method { + p.add_method(f.args[0].name, f) + } else { + p.table.register_fn(f) + } // println("generating gen inst $f.name(${f.str_args(p.table)}) $f.typ : $ti.inst") p.cgen.is_tmp = false diff --git a/vlib/compiler/gen_c.v b/vlib/compiler/gen_c.v index 032d870fd4..5aea8e1054 100644 --- a/vlib/compiler/gen_c.v +++ b/vlib/compiler/gen_c.v @@ -247,11 +247,9 @@ fn (table mut Table) fn_gen_name(f &Fn) string { return name } -fn (p mut Parser) gen_method_call(receiver_type, ftyp string, cgen_name string, - receiver Var,method_ph int) -{ +fn (p mut Parser) gen_method_call(receiver &Var, receiver_type string, cgen_name string, ftyp string) string { //mut cgen_name := p.table.fn_gen_name(f) - mut method_call := cgen_name + '(' + mut method_call := cgen_name + ' (' // if receiver is key_mut or a ref (&), generate & for the first arg if receiver.ref || (receiver.is_mut && !receiver_type.contains('*')) { method_call += '& /* ? */' @@ -268,12 +266,11 @@ fn (p mut Parser) gen_method_call(receiver_type, ftyp string, cgen_name string, // array_int => int cast = receiver_type.all_after('array_') cast = '*($cast*) ' - }else{ + } else { cast = '(voidptr) ' } } - p.cgen.set_placeholder(method_ph, '$cast $method_call') - //return method_call + return '$cast $method_call' } fn (p mut Parser) gen_array_at(typ_ string, is_arr0 bool, fn_ph int) { diff --git a/vlib/compiler/parser.v b/vlib/compiler/parser.v index 0e4894f6ff..b031c8a6f3 100644 --- a/vlib/compiler/parser.v +++ b/vlib/compiler/parser.v @@ -1617,7 +1617,10 @@ fn (p mut Parser) name_expr() string { } p.gen('(') mut typ := name - p.cast(name) + if typ in p.cur_fn.dispatch_of.inst.keys() { + typ = p.cur_fn.dispatch_of.inst[typ] + } + p.cast(typ) p.gen(')') for p.tok == .dot { typ = p.dot(typ, ph) diff --git a/vlib/compiler/tests/generic_test.v b/vlib/compiler/tests/generic_test.v index 9c7ace8db1..d3179985e1 100644 --- a/vlib/compiler/tests/generic_test.v +++ b/vlib/compiler/tests/generic_test.v @@ -2,15 +2,15 @@ fn simple(p T) T { return p } -fn sum(l []T, nil T) T { - mut r := nil +fn sum(l []T) T { + mut r := T(0) for e in l { r += e } return r } -fn map_f(l []T, f fn(T)U) []U { +fn map_f(l []T, f fn(T)U) []U { mut r := []U for e in l { r << f(e) @@ -44,15 +44,39 @@ fn assert_eq(a, b T) { assert r } +fn print_nice(x T, indent int) { + mut space := '' + for i in 0..indent { + space = space + ' ' + } + println('$space$x') +} + fn test_generic_fn() { assert_eq(simple(0+1), 1) assert_eq(simple('g') + 'h', 'gh') - assert_eq(sum([5.1,6.2,7.0], 0.0), 18.3) + assert_eq(sum([5.1,6.2,7.0]), 18.3) assert_eq(plus(i64(4), i64(6)), i64(10)) a := [1,2,3,4] - $if !windows { - b := map_f(a, square) - assert_eq(sum(b, 0), 30) // 1+4+9+16 = 30 - assert_eq(foldl(b, 1, mul_int), 576) // 1*4*9*16 = 576 - } + b := map_f(a, square) + assert_eq(sum(b), 30) // 1+4+9+16 = 30 + assert_eq(foldl(b, 1, mul_int), 576) // 1*4*9*16 = 576 + print_nice('str', 8) +} + +struct Point { +mut: + x f64 + y f64 +} + +fn (p mut Point) translate(x, y T) { + p.x += x + p.y += y +} + +fn test_generic_method() { + mut p := Point{} + p.translate(2, 1.0) + assert p.x == 2.0 && p.y == 1.0 }