generics: lots of fixes

pull/5000/head
Alexander Medvednikov 2020-05-24 04:43:00 +02:00
parent 4608898bcd
commit 67750c91d7
10 changed files with 247 additions and 208 deletions

View File

@ -10,10 +10,6 @@ import v.checker
import v.parser import v.parser
import v.depgraph import v.depgraph
const (
max_nr_errors = 100
)
pub struct Builder { pub struct Builder {
pub: pub:
table &table.Table table &table.Table
@ -26,6 +22,7 @@ mut:
global_scope &ast.Scope global_scope &ast.Scope
out_name_c string out_name_c string
out_name_js string out_name_js string
max_nr_errors int = 100
pub mut: pub mut:
module_search_paths []string 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) ferror := util.formatted_error(kind, err.message, err.file_path, err.pos)
eprintln(ferror) eprintln(ferror)
// eprintln('') // eprintln('')
if i > max_nr_errors { if i > b.max_nr_errors {
return return
} }
} }
@ -248,7 +245,7 @@ fn (b &Builder) print_warnings_and_errors() {
ferror := util.formatted_error(kind, err.message, err.file_path, err.pos) ferror := util.formatted_error(kind, err.message, err.file_path, err.pos)
eprintln(ferror) eprintln(ferror)
// eprintln('') // eprintln('')
if i > max_nr_errors { if i > b.max_nr_errors {
return return
} }
} }

View File

@ -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
}

View File

@ -274,7 +274,7 @@ pub fn (mut c Checker) struct_decl(decl ast.StructDecl) {
if field.has_default_expr { if field.has_default_expr {
c.expected_type = field.typ c.expected_type = field.typ
field_expr_type := c.expr(field.default_expr) 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_expr_type_sym := c.table.get_type_symbol(field_expr_type)
field_type_sym := c.table.get_type_symbol(field.typ) 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}`', 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 := c.expr(field.expr)
expr_type_sym := c.table.get_type_symbol(expr_type) expr_type_sym := c.table.get_type_symbol(expr_type)
field_type_sym := c.table.get_type_symbol(info_field.typ) 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`', c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$info_field.name`',
field.pos) field.pos)
} }
@ -467,11 +467,11 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
return table.void_type return table.void_type
} }
// the expressions have different types (array_x and x) // 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 // []T << T
return table.void_type 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 // []T << []T
return table.void_type 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) infix_expr.pos)
} }
// Dual sides check (compatibility check) // 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 // for type-unresolved consts
if left_type == table.void_type || right_type == table.void_type { if left_type == table.void_type || right_type == table.void_type {
return table.void_type return table.void_type
@ -673,7 +673,7 @@ fn (mut c Checker) assign_expr(mut assign_expr ast.AssignExpr) {
else {} else {}
} }
// Dual sides check (compatibility check) // 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) left_type_sym := c.table.get_type_symbol(left_type)
right_type_sym := c.table.get_type_symbol(right_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`', 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()) c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position())
continue 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) got_arg_sym := c.table.get_type_symbol(got_arg_typ)
// str method, allow type with str method if fn arg is string // str method, allow type with str method if fn arg is string
if exp_arg_sym.kind == .string && got_arg_sym.has_method('str') { 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 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 // str method, allow type with str method if fn arg is string
if arg_typ_sym.kind == .string && typ_sym.has_method('str') { if arg_typ_sym.kind == .string && typ_sym.has_method('str') {
continue continue
@ -1064,7 +1064,7 @@ pub fn (mut c Checker) check_or_block(mut call_expr ast.CallExpr, ret_type table
match last_stmt { match last_stmt {
ast.ExprStmt { ast.ExprStmt {
it.typ = c.expr(it.expr) 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) is_panic_or_exit := is_expr_panic_or_exit(it.expr)
if type_fits || is_panic_or_exit { if type_fits || is_panic_or_exit {
return return
@ -1163,8 +1163,11 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
} }
for i, exp_type in expected_types { for i, exp_type in expected_types {
got_typ := got_types[i] 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) } exp_type) }
*/
ok := c.check_types(got_typ, exp_type)
if !ok { // !c.table.check(got_typ, exp_typ) { if !ok { // !c.table.check(got_typ, exp_typ) {
got_typ_sym := c.table.get_type_symbol(got_typ) got_typ_sym := c.table.get_type_symbol(got_typ)
exp_typ_sym := c.table.get_type_symbol(exp_type) 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) c.fail_if_immutable(ident)
var_type := c.expr(ident) var_type := c.expr(ident)
assign_stmt.left_types << var_type 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) val_type_sym := c.table.get_type_symbol(val_type)
var_type_sym := c.table.get_type_symbol(var_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`', 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 c.expected_type = typ
continue 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) elem_type_sym := c.table.get_type_symbol(elem_type)
c.error('expected array element with type `$elem_type_sym.name`', array_init.pos) 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 info := ident.info as ast.IdentVar
if info.typ == table.t_type { if info.typ == table.t_type {
// Got a var with type T, return current generic type // Got a var with type T, return current generic type
return c.cur_generic_type // return c.cur_generic_type
} }
return info.typ return info.typ
} else if ident.kind == .constant { } 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 is_optional: is_optional
} }
if typ == table.t_type { 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 // Got a var with type T, return current generic type
typ = c.cur_generic_type // typ = c.cur_generic_type
} }
// } else {
it.typ = typ it.typ = typ
// unwrap optional (`println(x)`) // unwrap optional (`println(x)`)
if is_optional { 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 c.expected_type = cond_type
typ := c.expr(expr) typ := c.expr(expr)
typ_sym := c.table.get_type_symbol(typ) 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) exp_sym := c.table.get_type_symbol(cond_type)
c.error('cannot use `$typ_sym.name` as `$exp_sym.name` in `match`', node.pos) 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] val := node.vals[i]
key_type := c.expr(key) key_type := c.expr(key)
val_type := c.expr(val) 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) key0_type_sym := c.table.get_type_symbol(key0_type)
key_type_sym := c.table.get_type_symbol(key_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', c.error('map init: cannot use `$key_type_sym.name` as `$key0_type_sym.name` for map key',
node.pos) 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) val0_type_sym := c.table.get_type_symbol(val0_type)
val_type_sym := c.table.get_type_symbol(val_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', c.error('map init: cannot use `$val_type_sym.name` as `$val0_type_sym.name` for map value',

View File

@ -14,23 +14,24 @@ const (
pub struct Fmt { pub struct Fmt {
pub: pub:
out strings.Builder out strings.Builder
out_imports strings.Builder out_imports strings.Builder
table &table.Table table &table.Table
pub mut: pub mut:
indent int indent int
empty_line bool empty_line bool
line_len int line_len int
single_line_if bool single_line_if bool
cur_mod string cur_mod string
file ast.File file ast.File
did_imports bool did_imports bool
is_assign bool is_assign bool
auto_imports []string // automatically inserted imports that the user forgot to specify 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 import_pos int // position of the imports in the resulting string for later autoimports insertion
used_imports []string // to remove unused imports used_imports []string // to remove unused imports
is_debug bool is_debug bool
mod2alias map[string]string // for `import time as t`, will contain: 'time'=>'t' 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 { 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 { for imp in file.imports {
f.mod2alias[imp.mod.all_after_last('.')] = imp.alias f.mod2alias[imp.mod.all_after_last('.')] = imp.alias
} }
f.cur_mod = 'main' f.cur_mod = 'main'
for stmt in file.stmts { for stmt in file.stmts {
if stmt is ast.Import { 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) { 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.is_method {
if node.left is ast.Ident { if node.left is ast.Ident {
it := node.left as 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.write(')')
f.or_expr(node.or_block) f.or_expr(node.or_block)
} }
f.use_short_fn_args = false
} }
pub fn (mut f Fmt) match_expr(it ast.MatchExpr) { 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('}') f.write('}')
} else { } else {
f.writeln('$name{') if f.use_short_fn_args {
f.writeln('')
} else {
f.writeln('$name{')
}
f.indent++ f.indent++
for field in it.fields { for field in it.fields {
f.write('$field.name: ') f.write('$field.name: ')
@ -1109,7 +1124,9 @@ pub fn (mut f Fmt) struct_init(it ast.StructInit) {
f.writeln('') f.writeln('')
} }
f.indent-- f.indent--
f.write('}') if !f.use_short_fn_args {
f.write('}')
}
} }
} }

View File

@ -293,7 +293,7 @@ pub fn (mut g Gen) write_typeof_functions() {
// V type to C type // V type to C type
fn (mut g Gen) typ(t table.Type) string { fn (mut g Gen) typ(t table.Type) string {
mut styp := g.base_type(t) 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 // T => int etc
return g.typ(g.cur_generic_type) 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) g.expr(val)
} else { } else {
if val is ast.ArrayInit { 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.write('{$styp _ = ')
g.expr(val) g.expr(val)
g.writeln(';}') g.writeln(';}')
@ -979,7 +979,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
mut is_fixed_array_init := false mut is_fixed_array_init := false
mut has_val := false mut has_val := false
if val is ast.ArrayInit { 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 is_inside_ternary := g.inside_ternary != 0
cur_line := if is_inside_ternary { 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 { if node.left_type == table.string_type_idx && node.op == .plus_assign {
// str += str2 => `str = string_add(str, str2)` // str += str2 => `str = string_add(str, str2)`
g.expr(node.left) g.expr(node.left)
g.write(' = string_add(') g.write(' = /*f*/string_add(')
str_add = true str_add = true
} }
right_sym := g.table.get_type_symbol(node.right_type) 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 // string + string, string == string etc
// g.infix_op = node.op // 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 { if node.op == .key_is {
g.is_expr(node) g.is_expr(node)
return return
} }
right_sym := g.table.get_type_symbol(node.right_type) 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 { fn_name := match node.op {
.plus { 'ustring_add(' } .plus { 'ustring_add(' }
.eq { 'ustring_eq(' } .eq { 'ustring_eq(' }
@ -1585,7 +1586,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
g.write(', ') g.write(', ')
g.expr(node.right) g.expr(node.right)
g.write(')') 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 { fn_name := match node.op {
.plus { 'string_add(' } .plus { 'string_add(' }
.eq { 'string_eq(' } .eq { 'string_eq(' }
@ -1602,8 +1603,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
g.expr(node.right) g.expr(node.right)
g.write(')') g.write(')')
} else if node.op in [.eq, .ne] && left_sym.kind == .array && right_sym.kind == .array { } else if node.op in [.eq, .ne] && left_sym.kind == .array && right_sym.kind == .array {
styp := g.table.value_type(node.left_type) styp := g.table.value_type(left_type)
ptr_typ := g.typ(node.left_type).split('_')[1] ptr_typ := g.typ(left_type).split('_')[1]
if ptr_typ !in g.array_fn_definitions { if ptr_typ !in g.array_fn_definitions {
sym := g.table.get_type_symbol(left_sym.array_info().elem_type) sym := g.table.get_type_symbol(left_sym.array_info().elem_type)
g.generate_array_equality_fn(ptr_typ, styp, sym) g.generate_array_equality_fn(ptr_typ, styp, sym)
@ -1634,7 +1635,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
} }
else {} else {}
} }
styp := g.typ(node.left_type) styp := g.typ(left_type)
g.write('_IN($styp, ') g.write('_IN($styp, ')
g.expr(node.left) g.expr(node.left)
g.write(', ') g.write(', ')
@ -1653,18 +1654,18 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
g.expr(node.left) g.expr(node.left)
g.write(')') 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 // arr << val
tmp := g.new_tmp_var() 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 info := sym.info as table.Array
if right_sym.kind == .array && info.elem_type != node.right_type { 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`) // push an array => PUSH_MANY, but not if pushing an array to 2d array (`[][]int << []int`)
g.write('_PUSH_MANY(&') g.write('_PUSH_MANY(&')
g.expr(node.left) g.expr(node.left)
g.write(', (') g.write(', (')
g.expr_with_cast(node.right, node.right_type, node.left_type) g.expr_with_cast(node.right, node.right_type, left_type)
styp := g.typ(node.left_type) styp := g.typ(left_type)
g.write('), $tmp, $styp)') g.write('), $tmp, $styp)')
} else { } else {
// push a single element // push a single element
@ -1682,10 +1683,9 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
} }
g.write(' }))') g.write(' }))')
} }
} else if (node.left_type == node.right_type) && node.left_type.is_float() && node.op in } else if (left_type == node.right_type) && left_type.is_float() && node.op in [.eq, .ne] {
[.eq, .ne] {
// floats should be compared with epsilon // 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 { if node.op == .eq {
g.write('f64_eq(') g.write('f64_eq(')
} else { } 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 == left_sym.name.contains('.')) && left_sym.kind != .alias || left_sym.kind == .alias && (left_sym.info as table.Alias).language ==
.c { .c {
// !left_sym.is_number() { // !left_sym.is_number() {
g.write(g.typ(node.left_type)) g.write(g.typ(left_type))
g.write('_') g.write('_')
g.write(util.replace_op(node.op.str())) g.write(util.replace_op(node.op.str()))
g.write('(') 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 { if g.pref.is_debug {
paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) 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 );') 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);') g.writeln('\tv_panic(${cvar_name}.v_error);')
} }
} else { } else {

View File

@ -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 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 // loop thru each generic type and generate a function
for gen_type in g.table.fn_gen_types[it.name] { 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.cur_generic_type = gen_type
g.gen_fn_decl(it) 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) caname := c_name(arg.name)
arg_type_sym := g.table.get_type_symbol(arg.typ) arg_type_sym := g.table.get_type_symbol(arg.typ)
mut arg_type_name := g.typ(arg.typ) // arg_type_sym.name.replace('.', '__') 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 is_varg := i == args.len - 1 && is_variadic
if is_varg { if is_varg {
varg_type_str := int(arg.typ).str() 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 fargtypes << arg_type_name
} else { } else {
mut nr_muls := arg.typ.nr_muls() mut nr_muls := arg.typ.nr_muls()
s := arg_type_name + ' ' + caname s := arg_type_name + '/*F*/ ' + caname
if arg.is_mut { if arg.is_mut {
// mut arg needs one * // mut arg needs one *
nr_muls = 1 nr_muls = 1

View File

@ -109,6 +109,7 @@ pub mut:
skip_warnings bool // like C's "-w" skip_warnings bool // like C's "-w"
use_color ColorOutput // whether the warnings/errors should use ANSI color escapes. use_color ColorOutput // whether the warnings/errors should use ANSI color escapes.
is_parallel bool is_parallel bool
error_limit int
} }
pub fn parse_args(args []string) (&Preferences, string) { pub fn parse_args(args []string) (&Preferences, string) {
@ -201,6 +202,9 @@ pub fn parse_args(args []string) (&Preferences, string) {
'-print_v_files' { '-print_v_files' {
res.print_v_files = true res.print_v_files = true
} }
'-error-limit' {
res.error_limit =cmdline.option(current_args, '-error-limit', '0').int()
}
'-os' { '-os' {
target_os := cmdline.option(current_args, '-os', '') target_os := cmdline.option(current_args, '-os', '')
i++ i++

View File

@ -359,7 +359,7 @@ pub fn (t TypeSymbol) str() string {
pub fn (mut t Table) register_builtin_type_symbols() { pub fn (mut t Table) register_builtin_type_symbols() {
// reserve index 0 so nothing can go there // reserve index 0 so nothing can go there
// save index check, 0 will mean not found // save index check, 0 will mean not found
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .placeholder kind: .placeholder
name: 'reserved_0' name: 'reserved_0'
}) })
@ -368,72 +368,72 @@ pub fn (mut t Table) register_builtin_type_symbols() {
name: 'void' name: 'void'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .voidptr kind: .voidptr
name: 'voidptr' name: 'voidptr'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .byteptr kind: .byteptr
name: 'byteptr' name: 'byteptr'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .charptr kind: .charptr
name: 'charptr' name: 'charptr'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .i8 kind: .i8
name: 'i8' name: 'i8'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .i16 kind: .i16
name: 'i16' name: 'i16'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .int kind: .int
name: 'int' name: 'int'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .i64 kind: .i64
name: 'i64' name: 'i64'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .byte kind: .byte
name: 'byte' name: 'byte'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .u16 kind: .u16
name: 'u16' name: 'u16'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .u32 kind: .u32
name: 'u32' name: 'u32'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .u64 kind: .u64
name: 'u64' name: 'u64'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .f32 kind: .f32
name: 'f32' name: 'f32'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .f64 kind: .f64
name: 'f64' name: 'f64'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .char kind: .char
name: 'char' name: 'char'
mod: 'builtin' mod: 'builtin'
@ -448,37 +448,37 @@ pub fn (mut t Table) register_builtin_type_symbols() {
name: 'none' name: 'none'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .string kind: .string
name: 'string' name: 'string'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .ustring kind: .ustring
name: 'ustring' name: 'ustring'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .array kind: .array
name: 'array' name: 'array'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .map kind: .map
name: 'map' name: 'map'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .any kind: .any
name: 'any' name: 'any'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .any kind: .any
name: 'T' name: 'T'
mod: 'builtin' mod: 'builtin'
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .size_t kind: .size_t
name: 'size_t' name: 'size_t'
mod: 'builtin' mod: 'builtin'
@ -486,13 +486,13 @@ pub fn (mut t Table) register_builtin_type_symbols() {
// TODO: remove. for v1 map compatibility // TODO: remove. for v1 map compatibility
map_string_string_idx := t.find_or_register_map(string_type, string_type) 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) map_string_int_idx := t.find_or_register_map(string_type, int_type)
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .alias kind: .alias
name: 'map_string' name: 'map_string'
mod: 'builtin' mod: 'builtin'
parent_idx: map_string_string_idx parent_idx: map_string_string_idx
}) })
t.register_type_symbol(TypeSymbol{ t.register_type_symbol({
kind: .alias kind: .alias
name: 'map_int' name: 'map_int'
mod: 'builtin' mod: 'builtin'

View File

@ -438,123 +438,6 @@ pub fn (t &Table) value_type(typ Type) Type {
return void_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 // Once we have a module format we can read from module file instead
// this is not optimal // this is not optimal
pub fn (table &Table) qualify_module(mod, file_path string) string { pub fn (table &Table) qualify_module(mod, file_path string) string {

View File

@ -5,12 +5,12 @@ fn simple<T>(p T) T {
return p return p
} }
fn plus<T>(a, b T) T { fn plus<T>(xxx, b T) T {
// x := a // x := a
// y := b // y := b
// ww := ww // ww := ww
// q := xx + 1 // q := xx + 1
return a + b return xxx + b
} }
fn test_generic_fn() { fn test_generic_fn() {
@ -19,10 +19,10 @@ fn test_generic_fn() {
assert simple<string>('g') == 'g' assert simple<string>('g') == 'g'
assert simple<string>('g') + 'h' == 'gh' assert simple<string>('g') + 'h' == 'gh'
a := plus<int>(2, 3) a := plus<int>(2, 3)
println(a)
assert a == 5 assert a == 5
assert plus<int>(10, 1) == 11 assert plus<int>(10, 1) == 11
// plus<string>('a', 'b') assert plus<string>('a', 'b') == 'ab'
println(a)
} }
/* /*