1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

generics: add more checks (#9539)

This commit is contained in:
Conner 2021-04-02 16:27:54 +02:00 committed by GitHub
parent e438b158a6
commit 9ba8d02a5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 23 deletions

View File

@ -437,41 +437,63 @@ pub fn (mut c Checker) infer_fn_types(f ast.Fn, mut call_expr ast.CallExpr) {
}
mut typ := ast.void_type
for i, param in f.params {
mut to_set := ast.void_type
arg_i := if i != 0 && call_expr.is_method { i - 1 } else { i }
if call_expr.args.len <= arg_i {
break
}
arg := call_expr.args[arg_i]
param_type_sym := c.table.get_type_symbol(param.typ)
if param.typ.has_flag(.generic) && param_type_sym.name == gt_name {
typ = c.table.mktyp(arg.typ)
to_set = c.table.mktyp(arg.typ)
if arg.expr.is_auto_deref_var() {
typ = typ.deref()
to_set = to_set.deref()
}
// If the parent fn param is a generic too
if to_set.has_flag(.generic) {
to_set = c.unwrap_generic(to_set)
}
} else {
arg_sym := c.table.get_type_symbol(arg.typ)
if arg_sym.kind == .array && param_type_sym.kind == .array {
mut arg_elem_info := arg_sym.info as ast.Array
mut param_elem_info := param_type_sym.info as ast.Array
mut arg_elem_sym := c.table.get_type_symbol(arg_elem_info.elem_type)
mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type)
for {
if arg_elem_sym.kind == .array && param_elem_sym.kind == .array
&& c.cur_fn.generic_params.filter(it.name == param_elem_sym.name).len == 0 {
arg_elem_info = arg_elem_sym.info as ast.Array
arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type)
param_elem_info = param_elem_sym.info as ast.Array
param_elem_sym = c.table.get_type_symbol(param_elem_info.elem_type)
} else {
to_set = arg_elem_info.elem_type
break
}
}
} else if param.typ.has_flag(.variadic) {
to_set = c.table.mktyp(arg.typ)
}
break
}
arg_sym := c.table.get_type_symbol(arg.typ)
if arg_sym.kind == .array && param_type_sym.kind == .array {
mut arg_elem_info := arg_sym.info as ast.Array
mut param_elem_info := param_type_sym.info as ast.Array
mut arg_elem_sym := c.table.get_type_symbol(arg_elem_info.elem_type)
mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type)
for {
if arg_elem_sym.kind == .array && param_elem_sym.kind == .array
&& c.cur_fn.generic_params.filter(it.name == param_elem_sym.name).len == 0 {
arg_elem_info = arg_elem_sym.info as ast.Array
arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type)
param_elem_info = param_elem_sym.info as ast.Array
param_elem_sym = c.table.get_type_symbol(param_elem_info.elem_type)
} else {
typ = arg_elem_info.elem_type
break
if to_set != ast.void_type {
if typ != ast.void_type {
// try to promote
// only numbers so we don't promote pointers
if typ.is_number() && to_set.is_number() {
promoted := c.promote_num(typ, to_set)
if promoted != ast.void_type {
to_set = promoted
}
}
if !c.check_types(typ, to_set) {
c.error('inferred generic type `$gt_name` is ambigous got `${c.table.get_type_symbol(to_set).name}`, expected `${c.table.get_type_symbol(typ).name}`',
arg.pos)
}
}
break
} else if param.typ.has_flag(.variadic) {
typ = c.table.mktyp(arg.typ)
break
typ = to_set
}
}
if typ == ast.void_type {

View File

@ -1560,6 +1560,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) ast.Type {
c.error('expected $nr_args arguments, but got $call_expr.args.len', unexpected_arguments_pos)
return method.return_type
}
// if method_name == 'clone' {
// println('CLONE nr args=$method.args.len')
// }
@ -1672,6 +1673,10 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) ast.Type {
if call_expr.generic_types.len > 0 && method.generic_names.len == 0 {
c.error('a non generic function called like a generic one', call_expr.generic_list_pos)
}
if call_expr.generic_types.len > method.generic_names.len {
c.error('too many generic parameters got $call_expr.generic_types.len, expected $method.generic_names.len',
call_expr.generic_list_pos)
}
if method.generic_names.len > 0 {
return call_expr.return_type
}
@ -2209,6 +2214,11 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) ast.Type {
if call_expr.generic_types.len > 0 && f.generic_names.len == 0 {
c.error('a non generic function called like a generic one', call_expr.generic_list_pos)
}
if call_expr.generic_types.len > f.generic_names.len {
c.error('too many generic parameters got $call_expr.generic_types.len, expected $f.generic_names.len',
call_expr.generic_list_pos)
}
if f.generic_names.len > 0 {
return call_expr.return_type
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/generics_too_many_parameters.vv:6:8: error: too many generic parameters got 5, expected 1
4 |
5 | fn main() {
6 | foo<bool, int, bool, bool, int>(1)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 | }

View File

@ -0,0 +1,7 @@
fn foo<T>(b T) {
println(b)
}
fn main() {
foo<bool, int, bool, bool, int>(1)
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/generics_type_ambigous.vv:7:19: error: inferred generic type `B` is ambigous got `int`, expected `string`
5 |
6 | fn main() {
7 | test(2, 2, "2", 2)
| ^
8 | }

View File

@ -0,0 +1,8 @@
fn test<T, B> (a T, b T, c B, d B) {
println("$a $b $c $d")
}
fn main() {
test(2, 2, "2", 2)
}