From d6037cbcf2c3f6a7a56b66d01b3efd60d6fc22fa Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 28 May 2020 05:50:57 +0200 Subject: [PATCH] generics: fix method calls --- vlib/v/checker/checker.v | 44 ++++++++++++++++++++---------------- vlib/v/fmt/fmt.v | 10 ++++---- vlib/v/gen/fn.v | 29 +++++++++++++++--------- vlib/v/tests/generics_test.v | 10 ++++---- 4 files changed, 54 insertions(+), 39 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 084d000d41..f56a469972 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -366,7 +366,8 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$info_field.name`', field.pos) } - if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() && !expr_type.is_number() { + if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() && + !expr_type.is_number() { c.error('ref', field.pos) } struct_init.fields[i].typ = expr_type @@ -468,17 +469,18 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { if infix_expr.op in [.mod, .xor, .amp, .pipe] { side := if left_type == promoted_type { 'left' } else { 'right' } pos := if left_type == promoted_type { left_pos } else { right_pos } - name := if left_type == promoted_type { left.name } else { right.name } + name := if left_type == promoted_type { left.name } else { right.name } if infix_expr.op == .mod { c.error('float modulo not allowed, use math.fmod() instead', pos) } else { - c.error('$side type of `${infix_expr.op.str()}` cannot be non-integer type $name', pos) + c.error('$side type of `${infix_expr.op.str()}` cannot be non-integer type $name', + pos) } } } if infix_expr.op in [.div, .mod] { - if infix_expr.right is ast.IntegerLiteral && infix_expr.right.str() == '0' || - infix_expr.right is ast.FloatLiteral && infix_expr.right.str().f64() == 0.0 { + if infix_expr.right is ast.IntegerLiteral && infix_expr.right.str() == + '0' || infix_expr.right is ast.FloatLiteral && infix_expr.right.str().f64() == 0.0 { oper := if infix_expr.op == .div { 'division' } else { 'modulo' } c.error('$oper by zero', right_pos) } @@ -498,7 +500,8 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { c.type_implements(right_type, left_value_type, right_pos) } else { // []Animal << Cat - c.type_implements(c.table.value_type(right_type), left_value_type, right_pos) + c.type_implements(c.table.value_type(right_type), left_value_type, + right_pos) } return table.void_type } @@ -700,6 +703,7 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type { pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { left_type := c.expr(call_expr.left) + is_generic := left_type == table.t_type call_expr.left_type = left_type left_type_sym := c.table.get_type_symbol(c.unwrap_generic(left_type)) method_name := call_expr.name @@ -803,7 +807,12 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { call_expr.expected_arg_types << method.args[i].typ } } - call_expr.receiver_type = method.args[0].typ + if is_generic { + // We need the receiver to be T in cgen. + call_expr.receiver_type = table.t_type + } else { + call_expr.receiver_type = method.args[0].typ + } call_expr.return_type = method.return_type return method.return_type } @@ -1167,9 +1176,8 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { mut got_types := []table.Type{} for expr in return_stmt.exprs { typ := c.expr(expr) - // Unpack multi return types - sym := c.table.get_type_symbol(typ) + sym := c.table.get_type_symbol(typ) if sym.kind == .multi_return { for t in sym.mr_info().types { got_types << t @@ -1183,12 +1191,10 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { if exp_is_optional && got_types[0].idx() in [table.none_type_idx, c.table.type_idxs['Option']] { return } - if expected_types.len > 0 && expected_types.len != got_types.len { c.error('wrong number of return arguments', return_stmt.pos) return } - for i, exp_type in expected_types { got_typ := got_types[i] is_generic := exp_type == table.t_type @@ -1875,10 +1881,10 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type { // second use if ident.kind == .variable { info := ident.info as ast.IdentVar - if info.typ == table.t_type { - // Got a var with type T, return current generic type - // return c.cur_generic_type - } + // if info.typ == table.t_type { + // Got a var with type T, return current generic type + // return c.cur_generic_type + // } return info.typ } else if ident.kind == .constant { info := ident.info as ast.IdentVar @@ -2183,7 +2189,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { } } c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(last_expr.typ)}`', - node.pos) + node.pos) } } else { c.error('`if` expression requires an expression as the last statement of every branch', @@ -2194,7 +2200,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { // if only untyped literals were given default to int/f64 if node.typ == table.any_int_type { node.typ = table.int_type - } else if node.typ == table.any_flt_type { + } else if node.typ == table.any_flt_type { node.typ = table.f64_type } if expr_required { @@ -2410,8 +2416,8 @@ fn (mut c Checker) fn_decl(it ast.FnDecl) { // loop thru each generic type and generate a function for gen_type in c.table.fn_gen_types[it.name] { c.cur_generic_type = gen_type - //sym:=c.table.get_type_symbol(gen_type) - //println('\ncalling check for $it.name for type $sym.name') + // sym:=c.table.get_type_symbol(gen_type) + // println('\ncalling check for $it.name for type $sym.name') c.fn_decl(it) } c.cur_generic_type = 0 diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index e7105fc67b..f8c13bb80d 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -8,7 +8,9 @@ import v.table import strings const ( - tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t'] + tabs = ['', '\t', '\t\t', '\t\t\t', '\t\t\t\t', '\t\t\t\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t', + '\t\t\t\t\t\t\t\t' + ] max_len = 90 ) @@ -405,7 +407,8 @@ pub fn (mut f Fmt) type_decl(node ast.TypeDecl) { } f.write(')') if fn_info.return_type.idx() != table.void_type_idx { - ret_str := f.table.type_to_str(fn_info.return_type).replace(f.cur_mod + '.', '') + ret_str := f.table.type_to_str(fn_info.return_type).replace(f.cur_mod + '.', + '') f.write(' ' + ret_str) } } @@ -592,15 +595,12 @@ pub fn (mut f Fmt) expr(node ast.Expr) { if it.keys.len == 0 { mut ktyp := it.key_type mut vtyp := it.value_type - if vtyp == 0 { typ_sym := f.table.get_type_symbol(it.typ) minfo := typ_sym.info as table.Map - ktyp = minfo.key_type vtyp = minfo.value_type } - f.write('map[') f.write(f.type_to_str(ktyp)) f.write(']') diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index 8bf36a5203..5e2aa62b23 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -48,19 +48,19 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { 'irq_handler' { g.write('__IRQHANDLER ') } - + // GCC/clang attributes // prefixed by _ to indicate they're for advanced users only and not really supported by V. // source for descriptions: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes - // The cold attribute on functions is used to inform the compiler that the function is unlikely + // The cold attribute on functions is used to inform the compiler that the function is unlikely // to be executed. The function is optimized for size rather than speed and on many targets it // is placed into a special subsection of the text section so all cold functions appear close // together, improving code locality of non-cold parts of program. '_cold' { g.write('__attribute__((cold)) ') } - // The constructor attribute causes the function to be called automatically before execution + // The constructor attribute causes the function to be called automatically before execution // enters main (). '_constructor' { g.write('__attribute__((constructor)) ') @@ -70,7 +70,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { '_destructor' { g.write('__attribute__((destructor)) ') } - // Generally, inlining into a function is limited. For a function marked with this attribute, + // Generally, inlining into a function is limited. For a function marked with this attribute, // every call inside this function is inlined, if possible. '_flatten' { g.write('__attribute__((flatten)) ') @@ -80,16 +80,16 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { '_hot' { g.write('__attribute__((hot)) ') } - // This tells the compiler that a function is malloc-like, i.e., that the pointer P returned by - // the function cannot alias any other pointer valid when the function returns, and moreover no + // This tells the compiler that a function is malloc-like, i.e., that the pointer P returned by + // the function cannot alias any other pointer valid when the function returns, and moreover no // pointers to valid objects occur in any storage addressed by P. '_malloc' { g.write('__attribute__((malloc)) ') } - - // Calls to functions whose return value is not affected by changes to the observable state - // of the program and that have no observable effects on such state other than to return a - // value may lend themselves to optimizations such as common subexpression elimination. + + // Calls to functions whose return value is not affected by changes to the observable state + // of the program and that have no observable effects on such state other than to return a + // value may lend themselves to optimizations such as common subexpression elimination. // Declaring such functions with the const attribute allows GCC to avoid emitting some calls in // repeated invocations of the function with the same argument values. '_pure' { @@ -380,6 +380,13 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { } } +pub fn (mut g Gen) unwrap_generic(typ table.Type) table.Type { + if typ == table.t_type { + return g.cur_generic_type + } + return typ +} + fn (mut g Gen) method_call(node ast.CallExpr) { // TODO: there are still due to unchecked exprs (opt/some fn arg) if node.left_type == 0 { @@ -387,7 +394,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) { } // mut receiver_type_name := g.cc_type(node.receiver_type) // mut receiver_type_name := g.typ(node.receiver_type) - typ_sym := g.table.get_type_symbol(node.receiver_type) + typ_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver_type)) mut receiver_type_name := typ_sym.name.replace('.', '__') if typ_sym.kind == .interface_ { // Speaker_name_table[s._interface_idx].speak(s._object) diff --git a/vlib/v/tests/generics_test.v b/vlib/v/tests/generics_test.v index 16b57fbb09..c03b9f0662 100644 --- a/vlib/v/tests/generics_test.v +++ b/vlib/v/tests/generics_test.v @@ -40,10 +40,11 @@ fn test_foo() { fn create() { a := T{} - mut b := T{} - b.foo = 'foo' - println(b.foo) - assert b.foo == 'foo' + mut xx := T{} + xx.foo = 'foo' + println(xx.foo) + assert xx.foo == 'foo' + xx.init() } struct User { @@ -65,6 +66,7 @@ fn (c City) init() { fn test_create() { create() create() + // create() } /*