From ca8d23acabaf4f7d9081468e95a73e3fef98b9ce Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 5 Nov 2020 05:34:56 +0000 Subject: [PATCH] table: make Table.type_to_str generate proper function type, not fn name (#6716) --- vlib/v/checker/checker.v | 5 +++- vlib/v/checker/tests/fn_var.out | 17 +++++++++++++ vlib/v/checker/tests/fn_var.vv | 5 ++++ vlib/v/gen/cgen.v | 14 ++++------- vlib/v/table/atypes.v | 42 +++++++++++++++++++++++++-------- vlib/v/tests/typeof_test.v | 1 + 6 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 vlib/v/checker/tests/fn_var.out create mode 100644 vlib/v/checker/tests/fn_var.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index ecff92bb35..ba77e73ea7 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1585,7 +1585,10 @@ fn (mut c Checker) type_implements(typ table.Type, inter_typ table.Type, pos tok for imethod in inter_sym.methods { if method := typ_sym.find_method(imethod.name) { if !imethod.is_same_method_as(method) { - c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.source_name`, expected `${c.table.fn_to_str(imethod)}`', + sig := c.table.fn_signature(imethod, { + skip_receiver: true + }) + c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.source_name`, expected `$sig`', pos) return false } diff --git a/vlib/v/checker/tests/fn_var.out b/vlib/v/checker/tests/fn_var.out new file mode 100644 index 0000000000..6d244f9a4e --- /dev/null +++ b/vlib/v/checker/tests/fn_var.out @@ -0,0 +1,17 @@ +vlib/v/checker/tests/fn_var.vv:2:5: error: cannot assign to `f`: expected `fn (int) byte`, not `any_int` + 1 | mut f := fn(i int) byte {} + 2 | f = 4 + | ^ + 3 | mut p := &f + 4 | p = &[f] +vlib/v/checker/tests/fn_var.vv:4:5: error: cannot assign to `p`: expected `&fn (int) byte`, not `&[]fn (int) byte` + 2 | f = 4 + 3 | mut p := &f + 4 | p = &[f] + | ^ + 5 | f = fn(mut a []int) {} +vlib/v/checker/tests/fn_var.vv:5:5: error: cannot assign to `f`: expected `fn (int) byte`, not `fn (mut []int)` + 3 | mut p := &f + 4 | p = &[f] + 5 | f = fn(mut a []int) {} + | ~~ diff --git a/vlib/v/checker/tests/fn_var.vv b/vlib/v/checker/tests/fn_var.vv new file mode 100644 index 0000000000..a8049c34d7 --- /dev/null +++ b/vlib/v/checker/tests/fn_var.vv @@ -0,0 +1,5 @@ +mut f := fn(i int) byte {} +f = 4 +mut p := &f +p = &[f] +f = fn(mut a []int) {} diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 654e80eba2..28b60e77a0 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2389,21 +2389,15 @@ fn (mut g Gen) expr(node ast.Expr) { } // typeof(expr).name +// Note: typeof() should be a type known at compile-time, not a string +// sum types should not be handled dynamically fn (mut g Gen) typeof_name(node ast.TypeOf) { mut typ := node.expr_type if typ.has_flag(.generic) { typ = g.cur_generic_type } - sym := g.table.get_type_symbol(typ) - // TODO: fix table.type_to_str and use instead - if sym.kind == .function { - g.typeof_expr(node) - } else { - // Note: typeof() must be known at compile-time - // sum types should not be handled dynamically - s := g.table.type_to_str(typ) - g.write('tos_lit("${util.strip_main_name(s)}")') - } + s := g.table.type_to_str(typ) + g.write('tos_lit("${util.strip_main_name(s)}")') } fn (mut g Gen) typeof_expr(node ast.TypeOf) { diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index d2d62194d0..446f8c4902 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -839,7 +839,10 @@ pub fn (table &Table) type_to_str(t Type) string { } } .function { - // do nothing, source_name is sufficient + info := sym.info as FnType + res = table.fn_signature(info.func, { + type_only: true + }) } .map { if int(t) == map_type_idx { @@ -889,18 +892,37 @@ pub fn (table &Table) type_to_str(t Type) string { return res } -pub fn (t &Table) fn_to_str(func &Fn) string { +pub struct FnSignatureOpts { + skip_receiver bool + type_only bool +} + +pub fn (t &Table) fn_signature(func &Fn, opts FnSignatureOpts) string { mut sb := strings.new_builder(20) - sb.write('${func.name}(') - for i in 1 .. func.params.len { - param := func.params[i] - sb.write('$param.name') - if i == func.params.len - 1 || func.params[i + 1].typ != param.typ { - sb.write(' ${t.type_to_str(param.typ)}') - } - if i != func.params.len - 1 { + if !opts.skip_receiver { + sb.write('fn ') + // TODO write receiver + } + if !opts.type_only { + sb.write('$func.name') + } + sb.write('(') + start := int(opts.skip_receiver) + for i in start .. func.params.len { + if i != start { sb.write(', ') } + param := func.params[i] + mut typ := param.typ + if param.is_mut { + typ = typ.deref() + sb.write('mut ') + } + if !opts.type_only { + sb.write('$param.name ') + } + styp := t.type_to_str(typ) + sb.write('$styp') } sb.write(')') if func.return_type != void_type { diff --git a/vlib/v/tests/typeof_test.v b/vlib/v/tests/typeof_test.v index e314addd17..bbe3acefef 100644 --- a/vlib/v/tests/typeof_test.v +++ b/vlib/v/tests/typeof_test.v @@ -115,6 +115,7 @@ fn test_typeof_on_fn() { assert typeof(myfn3) == 'fn (int, string) byte' assert typeof(myfn4) == 'fn () i8' assert typeof(myfn).name == typeof(myfn) + assert typeof(&myfn).name == '&fn (int) int' assert typeof(myfn2).name == typeof(myfn2) assert typeof(myfn3).name == typeof(myfn3) assert typeof(myfn4).name == typeof(myfn4)