From 67750c91d7db20117261e61eb50a580fa8eb782d Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sun, 24 May 2020 04:43:00 +0200 Subject: [PATCH] generics: lots of fixes --- vlib/v/builder/builder.v | 9 +-- vlib/v/checker/check_types.v | 127 +++++++++++++++++++++++++++++++++++ vlib/v/checker/checker.v | 40 ++++++----- vlib/v/fmt/fmt.v | 55 +++++++++------ vlib/v/gen/cgen.v | 40 +++++------ vlib/v/gen/fn.v | 7 +- vlib/v/pref/pref.v | 4 ++ vlib/v/table/atypes.v | 48 ++++++------- vlib/v/table/table.v | 117 -------------------------------- vlib/v/tests/generic_test.v | 8 +-- 10 files changed, 247 insertions(+), 208 deletions(-) create mode 100644 vlib/v/checker/check_types.v diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index e1ac8b7f8b..c7f18691ff 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -10,10 +10,6 @@ import v.checker import v.parser import v.depgraph -const ( - max_nr_errors = 100 -) - pub struct Builder { pub: table &table.Table @@ -26,6 +22,7 @@ mut: global_scope &ast.Scope out_name_c string out_name_js string + max_nr_errors int = 100 pub mut: module_search_paths []string } @@ -233,7 +230,7 @@ fn (b &Builder) print_warnings_and_errors() { ferror := util.formatted_error(kind, err.message, err.file_path, err.pos) eprintln(ferror) // eprintln('') - if i > max_nr_errors { + if i > b.max_nr_errors { return } } @@ -248,7 +245,7 @@ fn (b &Builder) print_warnings_and_errors() { ferror := util.formatted_error(kind, err.message, err.file_path, err.pos) eprintln(ferror) // eprintln('') - if i > max_nr_errors { + if i > b.max_nr_errors { return } } diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v new file mode 100644 index 0000000000..30ab715a39 --- /dev/null +++ b/vlib/v/checker/check_types.v @@ -0,0 +1,127 @@ +// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module checker + +import v.table + +pub fn (c &Checker) check_types(got, expected table.Type) bool { + t := c.table + got_idx := got.idx() + exp_idx := expected.idx() + // got_is_ptr := got.is_ptr() + exp_is_ptr := expected.is_ptr() + // println('check: $got_type_sym.name, $exp_type_sym.name') + // # NOTE: use idxs here, and symbols below for perf + if got_idx == exp_idx { + // this is returning true even if one type is a ptr + // and the other is not, is this correct behaviour? + return true + } + if got_idx == table.none_type_idx && expected.flag_is(.optional) { + return true + } + // allow pointers to be initialized with 0. TODO: use none instead + if exp_is_ptr && got_idx == table.int_type_idx { + return true + } + if exp_idx == table.voidptr_type_idx || got_idx == table.voidptr_type_idx { + return true + } + if exp_idx == table.any_type_idx || got_idx == table.any_type_idx { + return true + } + // TODO i64 as int etc + if (exp_idx in table.pointer_type_idxs || exp_idx in table.number_type_idxs) && (got_idx in + table.pointer_type_idxs || got_idx in table.number_type_idxs) { + return true + } + // if exp_idx in pointer_type_idxs && got_idx in pointer_type_idxs { + // return true + // } + // see hack in checker IndexExpr line #691 + if (got_idx == table.byte_type_idx && exp_idx == table.byteptr_type_idx) || (exp_idx == + table.byte_type_idx && got_idx == table.byteptr_type_idx) { + return true + } + if (got_idx == table.char_type_idx && exp_idx == table.charptr_type_idx) || (exp_idx == + table.char_type_idx && got_idx == table.charptr_type_idx) { + return true + } + if expected == table.t_type && got == table.t_type { + return true + } + // # NOTE: use symbols from this point on for perf + got_type_sym := t.get_type_symbol(got) + exp_type_sym := t.get_type_symbol(expected) + // + if exp_type_sym.kind == .function && got_type_sym.kind == .int { + // TODO temporary + // fn == 0 + return true + } + // allow enum value to be used as int + if (got_type_sym.is_int() && exp_type_sym.kind == .enum_) || (exp_type_sym.is_int() && + got_type_sym.kind == .enum_) { + return true + } + // TODO + // if got_type_sym.kind == .array && exp_type_sym.kind == .array { + // return true + // } + if got_type_sym.kind == .array_fixed && exp_type_sym.kind == .byteptr { + info := got_type_sym.info as table.ArrayFixed + if info.elem_type.idx() == table.byte_type_idx { + return true + } + } + // TODO + if exp_type_sym.name == 'array' || got_type_sym.name == 'array' { + return true + } + // TODO + // accept [] when an expected type is an array + if got_type_sym.kind == .array && got_type_sym.name == 'array_void' && exp_type_sym.kind == + .array { + return true + } + // type alias + if (got_type_sym.kind == .alias && got_type_sym.parent_idx == exp_idx) || (exp_type_sym.kind == + .alias && exp_type_sym.parent_idx == got_idx) { + return true + } + // sum type + if got_type_sym.kind == .sum_type { + sum_info := got_type_sym.info as table.SumType + // TODO: handle `match SumType { &PtrVariant {} }` currently just checking base + if expected.set_nr_muls(0) in sum_info.variants { + return true + } + } + if exp_type_sym.kind == .sum_type { + sum_info := exp_type_sym.info as table.SumType + // TODO: handle `match SumType { &PtrVariant {} }` currently just checking base + if got.set_nr_muls(0) in sum_info.variants { + return true + } + } + // fn type + if got_type_sym.kind == .function && exp_type_sym.kind == .function { + got_info := got_type_sym.info as table.FnType + exp_info := exp_type_sym.info as table.FnType + got_fn := got_info.func + exp_fn := exp_info.func + // we are using check() to compare return type & args as they might include + // functions themselves. TODO: optimize, only use check() when needed + if got_fn.args.len == exp_fn.args.len && c.check_types(got_fn.return_type, exp_fn.return_type) { + for i, got_arg in got_fn.args { + exp_arg := exp_fn.args[i] + if !c.check_types(got_arg.typ, exp_arg.typ) { + return false + } + } + return true + } + } + return false +} diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 30dd0537ef..7ab6945427 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -274,7 +274,7 @@ pub fn (mut c Checker) struct_decl(decl ast.StructDecl) { if field.has_default_expr { c.expected_type = field.typ field_expr_type := c.expr(field.default_expr) - if !c.table.check(field_expr_type, field.typ) { + if !c.check_types(field_expr_type, field.typ) { field_expr_type_sym := c.table.get_type_symbol(field_expr_type) field_type_sym := c.table.get_type_symbol(field.typ) c.error('default expression for field `${field.name}` ' + 'has type `${field_expr_type_sym.name}`, but should be `${field_type_sym.name}`', @@ -363,7 +363,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { expr_type := c.expr(field.expr) expr_type_sym := c.table.get_type_symbol(expr_type) field_type_sym := c.table.get_type_symbol(info_field.typ) - if !c.table.check(expr_type, info_field.typ) { + if !c.check_types(expr_type, info_field.typ) { c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$info_field.name`', field.pos) } @@ -467,11 +467,11 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { return table.void_type } // the expressions have different types (array_x and x) - if c.table.check(right_type, left_value_type) { // , right_type) { + if c.check_types(right_type, left_value_type) { // , right_type) { // []T << T return table.void_type } - if right.kind == .array && c.table.check(left_value_type, c.table.value_type(right_type)) { + if right.kind == .array && c.check_types(left_value_type, c.table.value_type(right_type)) { // []T << []T return table.void_type } @@ -534,7 +534,7 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { infix_expr.pos) } // Dual sides check (compatibility check) - if !c.table.check(right_type, left_type) { + if !c.check_types(right_type, left_type) { // for type-unresolved consts if left_type == table.void_type || right_type == table.void_type { return table.void_type @@ -673,7 +673,7 @@ fn (mut c Checker) assign_expr(mut assign_expr ast.AssignExpr) { else {} } // Dual sides check (compatibility check) - if !c.table.check(right_type, left_type) { + if !c.check_types(right_type, left_type) { left_type_sym := c.table.get_type_symbol(left_type) right_type_sym := c.table.get_type_symbol(right_type) c.error('cannot assign `$right_type_sym.name` to variable `${assign_expr.left.str()}` of type `$left_type_sym.name`', @@ -775,7 +775,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position()) continue } - if !c.table.check(got_arg_typ, exp_arg_typ) { + if !c.check_types(got_arg_typ, exp_arg_typ) { got_arg_sym := c.table.get_type_symbol(got_arg_typ) // str method, allow type with str method if fn arg is string if exp_arg_sym.kind == .string && got_arg_sym.has_method('str') { @@ -968,7 +968,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { return true } */ - if !c.table.check(typ, arg.typ) { + if !c.check_types(typ, arg.typ) { // str method, allow type with str method if fn arg is string if arg_typ_sym.kind == .string && typ_sym.has_method('str') { continue @@ -1064,7 +1064,7 @@ pub fn (mut c Checker) check_or_block(mut call_expr ast.CallExpr, ret_type table match last_stmt { ast.ExprStmt { it.typ = c.expr(it.expr) - type_fits := c.table.check(it.typ, ret_type) + type_fits := c.check_types(it.typ, ret_type) is_panic_or_exit := is_expr_panic_or_exit(it.expr) if type_fits || is_panic_or_exit { return @@ -1163,8 +1163,11 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { } for i, exp_type in expected_types { got_typ := got_types[i] - ok := if exp_type == table.t_type { c.table.check(got_typ, c.cur_generic_type) } else { c.table.check(got_typ, + /* + ok := if exp_type == table.t_type { c.check_types(got_typ, c.cur_generic_type) } else { c.check_types(got_typ, exp_type) } + */ + ok := c.check_types(got_typ, exp_type) if !ok { // !c.table.check(got_typ, exp_typ) { got_typ_sym := c.table.get_type_symbol(got_typ) exp_typ_sym := c.table.get_type_symbol(exp_type) @@ -1264,7 +1267,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { c.fail_if_immutable(ident) var_type := c.expr(ident) assign_stmt.left_types << var_type - if !c.table.check(val_type, var_type) { + if !c.check_types(val_type, var_type) { val_type_sym := c.table.get_type_symbol(val_type) var_type_sym := c.table.get_type_symbol(var_type) c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`', @@ -1366,7 +1369,7 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type { c.expected_type = typ continue } - if !c.table.check(elem_type, typ) { + if !c.check_types(elem_type, typ) { elem_type_sym := c.table.get_type_symbol(elem_type) c.error('expected array element with type `$elem_type_sym.name`', array_init.pos) } @@ -1845,7 +1848,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type { 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 + // return c.cur_generic_type } return info.typ } else if ident.kind == .constant { @@ -1871,9 +1874,12 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type { is_optional: is_optional } if typ == table.t_type { + sym := c.table.get_type_symbol(c.cur_generic_type) + println('IDENT T unresolved $ident.name typ=$sym.name') // Got a var with type T, return current generic type - typ = c.cur_generic_type + // typ = c.cur_generic_type } + // } else { it.typ = typ // unwrap optional (`println(x)`) if is_optional { @@ -1975,7 +1981,7 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type { c.expected_type = cond_type typ := c.expr(expr) typ_sym := c.table.get_type_symbol(typ) - if !node.is_sum_type && !c.table.check(typ, cond_type) { + if !node.is_sum_type && !c.check_types(typ, cond_type) { exp_sym := c.table.get_type_symbol(cond_type) c.error('cannot use `$typ_sym.name` as `$exp_sym.name` in `match`', node.pos) } @@ -2260,13 +2266,13 @@ pub fn (mut c Checker) map_init(mut node ast.MapInit) table.Type { val := node.vals[i] key_type := c.expr(key) val_type := c.expr(val) - if !c.table.check(key_type, key0_type) { + if !c.check_types(key_type, key0_type) { key0_type_sym := c.table.get_type_symbol(key0_type) key_type_sym := c.table.get_type_symbol(key_type) c.error('map init: cannot use `$key_type_sym.name` as `$key0_type_sym.name` for map key', node.pos) } - if !c.table.check(val_type, val0_type) { + if !c.check_types(val_type, val0_type) { val0_type_sym := c.table.get_type_symbol(val0_type) val_type_sym := c.table.get_type_symbol(val_type) c.error('map init: cannot use `$val_type_sym.name` as `$val0_type_sym.name` for map value', diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 88b5b219ce..e0666ff14b 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -14,23 +14,24 @@ const ( pub struct Fmt { pub: - out strings.Builder - out_imports strings.Builder - table &table.Table + out strings.Builder + out_imports strings.Builder + table &table.Table pub mut: - indent int - empty_line bool - line_len int - single_line_if bool - cur_mod string - file ast.File - did_imports bool - is_assign bool - auto_imports []string // automatically inserted imports that the user forgot to specify - import_pos int // position of the imports in the resulting string for later autoimports insertion - used_imports []string // to remove unused imports - is_debug bool - mod2alias map[string]string // for `import time as t`, will contain: 'time'=>'t' + indent int + empty_line bool + line_len int + single_line_if bool + cur_mod string + file ast.File + did_imports bool + is_assign bool + auto_imports []string // automatically inserted imports that the user forgot to specify + import_pos int // position of the imports in the resulting string for later autoimports insertion + used_imports []string // to remove unused imports + is_debug bool + mod2alias map[string]string // for `import time as t`, will contain: 'time'=>'t' + use_short_fn_args bool } pub fn fmt(file ast.File, table &table.Table, is_debug bool) string { @@ -44,7 +45,7 @@ pub fn fmt(file ast.File, table &table.Table, is_debug bool) string { } for imp in file.imports { f.mod2alias[imp.mod.all_after_last('.')] = imp.alias - } + } f.cur_mod = 'main' for stmt in file.stmts { if stmt is ast.Import { @@ -838,6 +839,15 @@ pub fn (mut f Fmt) if_expr(it ast.IfExpr) { } pub fn (mut f Fmt) call_expr(node ast.CallExpr) { + /* + if node.args.len == 1 && node.expected_arg_types.len == 1 && node.args[0].expr is ast.StructInit && + node.args[0].typ == node.expected_arg_types[0] { + // struct_init := node.args[0].expr as ast.StructInit + // if struct_init.typ == node.args[0].typ { + f.use_short_fn_args = true + // } + } + */ if node.is_method { if node.left is ast.Ident { it := node.left as ast.Ident @@ -876,6 +886,7 @@ pub fn (mut f Fmt) call_expr(node ast.CallExpr) { f.write(')') f.or_expr(node.or_block) } + f.use_short_fn_args = false } pub fn (mut f Fmt) match_expr(it ast.MatchExpr) { @@ -1101,7 +1112,11 @@ pub fn (mut f Fmt) struct_init(it ast.StructInit) { } f.write('}') } else { - f.writeln('$name{') + if f.use_short_fn_args { + f.writeln('') + } else { + f.writeln('$name{') + } f.indent++ for field in it.fields { f.write('$field.name: ') @@ -1109,7 +1124,9 @@ pub fn (mut f Fmt) struct_init(it ast.StructInit) { f.writeln('') } f.indent-- - f.write('}') + if !f.use_short_fn_args { + f.write('}') + } } } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 1ff22ca7de..d66eb42ac5 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -293,7 +293,7 @@ pub fn (mut g Gen) write_typeof_functions() { // V type to C type fn (mut g Gen) typ(t table.Type) string { mut styp := g.base_type(t) - if styp.len == 1 && g.cur_generic_type != 0 { + if styp.len == 1 && t == table.t_type && g.cur_generic_type != 0 { // T => int etc return g.typ(g.cur_generic_type) } @@ -968,8 +968,8 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { g.expr(val) } else { if val is ast.ArrayInit { - g.gen_default_init_value( val as ast.ArrayInit ) - } + g.gen_default_init_value(val as ast.ArrayInit) + } g.write('{$styp _ = ') g.expr(val) g.writeln(';}') @@ -979,7 +979,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { mut is_fixed_array_init := false mut has_val := false if val is ast.ArrayInit { - is_fixed_array_init, has_val = g.gen_default_init_value( val as ast.ArrayInit ) + is_fixed_array_init, has_val = g.gen_default_init_value(val as ast.ArrayInit) } is_inside_ternary := g.inside_ternary != 0 cur_line := if is_inside_ternary { @@ -1487,7 +1487,7 @@ fn (mut g Gen) assign_expr(node ast.AssignExpr) { if node.left_type == table.string_type_idx && node.op == .plus_assign { // str += str2 => `str = string_add(str, str2)` g.expr(node.left) - g.write(' = string_add(') + g.write(' = /*f*/string_add(') str_add = true } right_sym := g.table.get_type_symbol(node.right_type) @@ -1563,13 +1563,14 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { // } // string + string, string == string etc // g.infix_op = node.op - left_sym := g.table.get_type_symbol(node.left_type) + left_type := if node.left_type == table.t_type { g.cur_generic_type } else { node.left_type } + left_sym := g.table.get_type_symbol(left_type) if node.op == .key_is { g.is_expr(node) return } right_sym := g.table.get_type_symbol(node.right_type) - if node.left_type == table.ustring_type_idx && node.op != .key_in && node.op != .not_in { + if left_type == table.ustring_type_idx && node.op != .key_in && node.op != .not_in { fn_name := match node.op { .plus { 'ustring_add(' } .eq { 'ustring_eq(' } @@ -1585,7 +1586,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { g.write(', ') g.expr(node.right) g.write(')') - } else if node.left_type == table.string_type_idx && node.op != .key_in && node.op != .not_in { + } else if left_type == table.string_type_idx && node.op != .key_in && node.op != .not_in { fn_name := match node.op { .plus { 'string_add(' } .eq { 'string_eq(' } @@ -1602,8 +1603,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { g.expr(node.right) g.write(')') } else if node.op in [.eq, .ne] && left_sym.kind == .array && right_sym.kind == .array { - styp := g.table.value_type(node.left_type) - ptr_typ := g.typ(node.left_type).split('_')[1] + styp := g.table.value_type(left_type) + ptr_typ := g.typ(left_type).split('_')[1] if ptr_typ !in g.array_fn_definitions { sym := g.table.get_type_symbol(left_sym.array_info().elem_type) g.generate_array_equality_fn(ptr_typ, styp, sym) @@ -1634,7 +1635,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { } else {} } - styp := g.typ(node.left_type) + styp := g.typ(left_type) g.write('_IN($styp, ') g.expr(node.left) g.write(', ') @@ -1653,18 +1654,18 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { g.expr(node.left) g.write(')') } - } else if node.op == .left_shift && g.table.get_type_symbol(node.left_type).kind == .array { + } else if node.op == .left_shift && g.table.get_type_symbol(left_type).kind == .array { // arr << val tmp := g.new_tmp_var() - sym := g.table.get_type_symbol(node.left_type) + sym := g.table.get_type_symbol(left_type) info := sym.info as table.Array if right_sym.kind == .array && info.elem_type != node.right_type { // push an array => PUSH_MANY, but not if pushing an array to 2d array (`[][]int << []int`) g.write('_PUSH_MANY(&') g.expr(node.left) g.write(', (') - g.expr_with_cast(node.right, node.right_type, node.left_type) - styp := g.typ(node.left_type) + g.expr_with_cast(node.right, node.right_type, left_type) + styp := g.typ(left_type) g.write('), $tmp, $styp)') } else { // push a single element @@ -1682,10 +1683,9 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { } g.write(' }))') } - } else if (node.left_type == node.right_type) && node.left_type.is_float() && node.op in - [.eq, .ne] { + } else if (left_type == node.right_type) && left_type.is_float() && node.op in [.eq, .ne] { // floats should be compared with epsilon - if node.left_type == table.f64_type_idx { + if left_type == table.f64_type_idx { if node.op == .eq { g.write('f64_eq(') } else { @@ -1706,7 +1706,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { left_sym.name.contains('.')) && left_sym.kind != .alias || left_sym.kind == .alias && (left_sym.info as table.Alias).language == .c { // !left_sym.is_number() { - g.write(g.typ(node.left_type)) + g.write(g.typ(left_type)) g.write('_') g.write(util.replace_op(node.op.str())) g.write('(') @@ -2976,7 +2976,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table. if g.pref.is_debug { paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) g.writeln('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ${cvar_name}.v_error );') - }else{ + } else { g.writeln('\tv_panic(${cvar_name}.v_error);') } } else { diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index d686c08900..3ae906b980 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -21,6 +21,8 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl) { if it.is_generic && g.cur_generic_type == 0 { // need the cur_generic_type check to avoid inf. recursion // loop thru each generic type and generate a function for gen_type in g.table.fn_gen_types[it.name] { + sym := g.table.get_type_symbol(gen_type) + println('gen fn `$it.name` for type `$sym.name`') g.cur_generic_type = gen_type g.gen_fn_decl(it) } @@ -234,6 +236,9 @@ fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) ([]string, []string) caname := c_name(arg.name) arg_type_sym := g.table.get_type_symbol(arg.typ) mut arg_type_name := g.typ(arg.typ) // arg_type_sym.name.replace('.', '__') + if arg.name == 'xxx' { + println('! ' + arg_type_name) + } is_varg := i == args.len - 1 && is_variadic if is_varg { varg_type_str := int(arg.typ).str() @@ -264,7 +269,7 @@ fn (mut g Gen) fn_args(args []table.Arg, is_variadic bool) ([]string, []string) fargtypes << arg_type_name } else { mut nr_muls := arg.typ.nr_muls() - s := arg_type_name + ' ' + caname + s := arg_type_name + '/*F*/ ' + caname if arg.is_mut { // mut arg needs one * nr_muls = 1 diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index db427e9390..aafaa0ea7a 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -109,6 +109,7 @@ pub mut: skip_warnings bool // like C's "-w" use_color ColorOutput // whether the warnings/errors should use ANSI color escapes. is_parallel bool + error_limit int } pub fn parse_args(args []string) (&Preferences, string) { @@ -201,6 +202,9 @@ pub fn parse_args(args []string) (&Preferences, string) { '-print_v_files' { res.print_v_files = true } + '-error-limit' { + res.error_limit =cmdline.option(current_args, '-error-limit', '0').int() + } '-os' { target_os := cmdline.option(current_args, '-os', '') i++ diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index 4053219f6c..e1db6b3adc 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -359,7 +359,7 @@ pub fn (t TypeSymbol) str() string { pub fn (mut t Table) register_builtin_type_symbols() { // reserve index 0 so nothing can go there // save index check, 0 will mean not found - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .placeholder name: 'reserved_0' }) @@ -368,72 +368,72 @@ pub fn (mut t Table) register_builtin_type_symbols() { name: 'void' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .voidptr name: 'voidptr' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .byteptr name: 'byteptr' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .charptr name: 'charptr' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .i8 name: 'i8' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .i16 name: 'i16' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .int name: 'int' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .i64 name: 'i64' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .byte name: 'byte' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .u16 name: 'u16' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .u32 name: 'u32' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .u64 name: 'u64' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .f32 name: 'f32' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .f64 name: 'f64' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .char name: 'char' mod: 'builtin' @@ -448,37 +448,37 @@ pub fn (mut t Table) register_builtin_type_symbols() { name: 'none' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .string name: 'string' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .ustring name: 'ustring' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .array name: 'array' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .map name: 'map' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .any name: 'any' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .any name: 'T' mod: 'builtin' }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .size_t name: 'size_t' mod: 'builtin' @@ -486,13 +486,13 @@ pub fn (mut t Table) register_builtin_type_symbols() { // TODO: remove. for v1 map compatibility map_string_string_idx := t.find_or_register_map(string_type, string_type) map_string_int_idx := t.find_or_register_map(string_type, int_type) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .alias name: 'map_string' mod: 'builtin' parent_idx: map_string_string_idx }) - t.register_type_symbol(TypeSymbol{ + t.register_type_symbol({ kind: .alias name: 'map_int' mod: 'builtin' diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index 6a14037d61..95a27683be 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -438,123 +438,6 @@ pub fn (t &Table) value_type(typ Type) Type { return void_type } -pub fn (t &Table) check(got, expected Type) bool { - got_idx := got.idx() - exp_idx := expected.idx() - // got_is_ptr := got.is_ptr() - exp_is_ptr := expected.is_ptr() - // println('check: $got_type_sym.name, $exp_type_sym.name') - // # NOTE: use idxs here, and symbols below for perf - if got_idx == exp_idx { - // this is returning true even if one type is a ptr - // and the other is not, is this correct behaviour? - return true - } - if got_idx == none_type_idx && expected.flag_is(.optional) { - return true - } - // allow pointers to be initialized with 0. TODO: use none instead - if exp_is_ptr && got_idx == int_type_idx { - return true - } - if exp_idx == voidptr_type_idx || got_idx == voidptr_type_idx { - return true - } - if exp_idx == any_type_idx || got_idx == any_type_idx { - return true - } - // TODO i64 as int etc - if (exp_idx in pointer_type_idxs || exp_idx in number_type_idxs) && (got_idx in pointer_type_idxs || - got_idx in number_type_idxs) { - return true - } - // if exp_idx in pointer_type_idxs && got_idx in pointer_type_idxs { - // return true - // } - // see hack in checker IndexExpr line #691 - if (got_idx == byte_type_idx && exp_idx == byteptr_type_idx) || (exp_idx == byte_type_idx && - got_idx == byteptr_type_idx) { - return true - } - if (got_idx == char_type_idx && exp_idx == charptr_type_idx) || (exp_idx == char_type_idx && - got_idx == charptr_type_idx) { - return true - } - // # NOTE: use symbols from this point on for perf - got_type_sym := t.get_type_symbol(got) - exp_type_sym := t.get_type_symbol(expected) - // - if exp_type_sym.kind == .function && got_type_sym.kind == .int { - // TODO temporary - // fn == 0 - return true - } - // allow enum value to be used as int - if (got_type_sym.is_int() && exp_type_sym.kind == .enum_) || (exp_type_sym.is_int() && - got_type_sym.kind == .enum_) { - return true - } - // TODO - // if got_type_sym.kind == .array && exp_type_sym.kind == .array { - // return true - // } - if got_type_sym.kind == .array_fixed && exp_type_sym.kind == .byteptr { - info := got_type_sym.info as ArrayFixed - if info.elem_type.idx() == byte_type_idx { - return true - } - } - // TODO - if exp_type_sym.name == 'array' || got_type_sym.name == 'array' { - return true - } - // TODO - // accept [] when an expected type is an array - if got_type_sym.kind == .array && got_type_sym.name == 'array_void' && exp_type_sym.kind == - .array { - return true - } - // type alias - if (got_type_sym.kind == .alias && got_type_sym.parent_idx == exp_idx) || (exp_type_sym.kind == - .alias && exp_type_sym.parent_idx == got_idx) { - return true - } - // sum type - if got_type_sym.kind == .sum_type { - sum_info := got_type_sym.info as SumType - // TODO: handle `match SumType { &PtrVariant {} }` currently just checking base - if expected.set_nr_muls(0) in sum_info.variants { - return true - } - } - if exp_type_sym.kind == .sum_type { - sum_info := exp_type_sym.info as SumType - // TODO: handle `match SumType { &PtrVariant {} }` currently just checking base - if got.set_nr_muls(0) in sum_info.variants { - return true - } - } - // fn type - if got_type_sym.kind == .function && exp_type_sym.kind == .function { - got_info := got_type_sym.info as FnType - exp_info := exp_type_sym.info as FnType - got_fn := got_info.func - exp_fn := exp_info.func - // we are using check() to compare return type & args as they might include - // functions themselves. TODO: optimize, only use check() when needed - if got_fn.args.len == exp_fn.args.len && t.check(got_fn.return_type, exp_fn.return_type) { - for i, got_arg in got_fn.args { - exp_arg := exp_fn.args[i] - if !t.check(got_arg.typ, exp_arg.typ) { - return false - } - } - return true - } - } - return false -} - // Once we have a module format we can read from module file instead // this is not optimal pub fn (table &Table) qualify_module(mod, file_path string) string { diff --git a/vlib/v/tests/generic_test.v b/vlib/v/tests/generic_test.v index 290f76aac8..9fbd3377e0 100644 --- a/vlib/v/tests/generic_test.v +++ b/vlib/v/tests/generic_test.v @@ -5,12 +5,12 @@ fn simple(p T) T { return p } -fn plus(a, b T) T { +fn plus(xxx, b T) T { // x := a // y := b // ww := ww // q := xx + 1 - return a + b + return xxx + b } fn test_generic_fn() { @@ -19,10 +19,10 @@ fn test_generic_fn() { assert simple('g') == 'g' assert simple('g') + 'h' == 'gh' a := plus(2, 3) + println(a) assert a == 5 assert plus(10, 1) == 11 - // plus('a', 'b') - println(a) + assert plus('a', 'b') == 'ab' } /*