mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
generics: check generic_fn called arg mismatch (#9510)
This commit is contained in:
parent
97f43d6a97
commit
6143bd6232
|
@ -276,6 +276,9 @@ pub fn (mut c Checker) check_types(got table.Type, expected table.Type) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if expected.has_flag(.generic) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,10 +489,10 @@ pub fn (mut c Checker) infer_fn_types(f table.Fn, mut call_expr ast.CallExpr) {
|
||||||
c.table.register_fn_gen_type(f.name, inferred_types)
|
c.table.register_fn_gen_type(f.name, inferred_types)
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve_generic_type resolves generics to real types T => int.
|
// resolve_generic_by_names resolves generics to real types T => int.
|
||||||
// Even map[string]map[string]T can be resolved.
|
// Even map[string]map[string]T can be resolved.
|
||||||
// This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl.
|
// This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl.
|
||||||
fn (mut c Checker) resolve_generic_type(generic_type table.Type, generic_names []string, generic_types []table.Type) ?table.Type {
|
fn (mut c Checker) resolve_generic_by_names(generic_type table.Type, generic_names []string, generic_types []table.Type) ?table.Type {
|
||||||
mut sym := c.table.get_type_symbol(generic_type)
|
mut sym := c.table.get_type_symbol(generic_type)
|
||||||
if sym.name in generic_names {
|
if sym.name in generic_names {
|
||||||
index := generic_names.index(sym.name)
|
index := generic_names.index(sym.name)
|
||||||
|
@ -509,14 +512,14 @@ fn (mut c Checker) resolve_generic_type(generic_type table.Type, generic_names [
|
||||||
elem_sym = c.table.get_type_symbol(elem_type)
|
elem_sym = c.table.get_type_symbol(elem_type)
|
||||||
dims++
|
dims++
|
||||||
}
|
}
|
||||||
if typ := c.resolve_generic_type(elem_type, generic_names, generic_types) {
|
if typ := c.resolve_generic_by_names(elem_type, generic_names, generic_types) {
|
||||||
idx := c.table.find_or_register_array_with_dims(typ, dims)
|
idx := c.table.find_or_register_array_with_dims(typ, dims)
|
||||||
array_typ := table.new_type(idx)
|
array_typ := table.new_type(idx)
|
||||||
return array_typ
|
return array_typ
|
||||||
}
|
}
|
||||||
} else if sym.kind == .chan {
|
} else if sym.kind == .chan {
|
||||||
info := sym.info as table.Chan
|
info := sym.info as table.Chan
|
||||||
if typ := c.resolve_generic_type(info.elem_type, generic_names, generic_types) {
|
if typ := c.resolve_generic_by_names(info.elem_type, generic_names, generic_types) {
|
||||||
idx := c.table.find_or_register_chan(typ, typ.nr_muls() > 0)
|
idx := c.table.find_or_register_chan(typ, typ.nr_muls() > 0)
|
||||||
chan_typ := table.new_type(idx)
|
chan_typ := table.new_type(idx)
|
||||||
return chan_typ
|
return chan_typ
|
||||||
|
@ -525,7 +528,7 @@ fn (mut c Checker) resolve_generic_type(generic_type table.Type, generic_names [
|
||||||
mut types := []table.Type{}
|
mut types := []table.Type{}
|
||||||
mut type_changed := false
|
mut type_changed := false
|
||||||
for ret_type in sym.info.types {
|
for ret_type in sym.info.types {
|
||||||
if typ := c.resolve_generic_type(ret_type, generic_names, generic_types) {
|
if typ := c.resolve_generic_by_names(ret_type, generic_names, generic_types) {
|
||||||
types << typ
|
types << typ
|
||||||
type_changed = true
|
type_changed = true
|
||||||
} else {
|
} else {
|
||||||
|
@ -541,11 +544,83 @@ fn (mut c Checker) resolve_generic_type(generic_type table.Type, generic_names [
|
||||||
mut type_changed := false
|
mut type_changed := false
|
||||||
mut unwrapped_key_type := sym.info.key_type
|
mut unwrapped_key_type := sym.info.key_type
|
||||||
mut unwrapped_value_type := sym.info.value_type
|
mut unwrapped_value_type := sym.info.value_type
|
||||||
if typ := c.resolve_generic_type(sym.info.key_type, generic_names, generic_types) {
|
if typ := c.resolve_generic_by_names(sym.info.key_type, generic_names, generic_types) {
|
||||||
unwrapped_key_type = typ
|
unwrapped_key_type = typ
|
||||||
type_changed = true
|
type_changed = true
|
||||||
}
|
}
|
||||||
if typ := c.resolve_generic_type(sym.info.value_type, generic_names, generic_types) {
|
if typ := c.resolve_generic_by_names(sym.info.value_type, generic_names, generic_types) {
|
||||||
|
unwrapped_value_type = typ
|
||||||
|
type_changed = true
|
||||||
|
}
|
||||||
|
if type_changed {
|
||||||
|
idx := c.table.find_or_register_map(unwrapped_key_type, unwrapped_value_type)
|
||||||
|
typ := table.new_type(idx)
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve_generic_by_types resolves generics to real types T => int.
|
||||||
|
// Even map[string]map[string]T can be resolved.
|
||||||
|
// This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl.
|
||||||
|
fn (mut c Checker) resolve_generic_by_types(generic_type table.Type, from_types []table.Type, to_types []table.Type) ?table.Type {
|
||||||
|
mut sym := c.table.get_type_symbol(generic_type)
|
||||||
|
if generic_type in from_types {
|
||||||
|
index := from_types.index(generic_type)
|
||||||
|
mut typ := to_types[index]
|
||||||
|
typ = typ.set_nr_muls(generic_type.nr_muls())
|
||||||
|
if generic_type.has_flag(.optional) {
|
||||||
|
typ = typ.set_flag(.optional)
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
} else if sym.kind == .array {
|
||||||
|
info := sym.info as table.Array
|
||||||
|
mut elem_type := info.elem_type
|
||||||
|
mut elem_sym := c.table.get_type_symbol(elem_type)
|
||||||
|
mut dims := 1
|
||||||
|
for mut elem_sym.info is table.Array {
|
||||||
|
elem_type = elem_sym.info.elem_type
|
||||||
|
elem_sym = c.table.get_type_symbol(elem_type)
|
||||||
|
dims++
|
||||||
|
}
|
||||||
|
if typ := c.resolve_generic_by_types(elem_type, from_types, to_types) {
|
||||||
|
idx := c.table.find_or_register_array_with_dims(typ, dims)
|
||||||
|
array_typ := table.new_type(idx)
|
||||||
|
return array_typ
|
||||||
|
}
|
||||||
|
} else if sym.kind == .chan {
|
||||||
|
info := sym.info as table.Chan
|
||||||
|
if typ := c.resolve_generic_by_types(info.elem_type, from_types, to_types) {
|
||||||
|
idx := c.table.find_or_register_chan(typ, typ.nr_muls() > 0)
|
||||||
|
chan_typ := table.new_type(idx)
|
||||||
|
return chan_typ
|
||||||
|
}
|
||||||
|
} else if mut sym.info is table.MultiReturn {
|
||||||
|
mut types := []table.Type{}
|
||||||
|
mut type_changed := false
|
||||||
|
for ret_type in sym.info.types {
|
||||||
|
if typ := c.resolve_generic_by_types(ret_type, from_types, to_types) {
|
||||||
|
types << typ
|
||||||
|
type_changed = true
|
||||||
|
} else {
|
||||||
|
types << ret_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if type_changed {
|
||||||
|
idx := c.table.find_or_register_multi_return(types)
|
||||||
|
typ := table.new_type(idx)
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
} else if mut sym.info is table.Map {
|
||||||
|
mut type_changed := false
|
||||||
|
mut unwrapped_key_type := sym.info.key_type
|
||||||
|
mut unwrapped_value_type := sym.info.value_type
|
||||||
|
if typ := c.resolve_generic_by_types(sym.info.key_type, from_types, to_types) {
|
||||||
|
unwrapped_key_type = typ
|
||||||
|
type_changed = true
|
||||||
|
}
|
||||||
|
if typ := c.resolve_generic_by_types(sym.info.value_type, from_types, to_types) {
|
||||||
unwrapped_value_type = typ
|
unwrapped_value_type = typ
|
||||||
type_changed = true
|
type_changed = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1598,7 +1598,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
c.infer_fn_types(method, mut call_expr)
|
c.infer_fn_types(method, mut call_expr)
|
||||||
}
|
}
|
||||||
if call_expr.generic_types.len > 0 && method.return_type != 0 {
|
if call_expr.generic_types.len > 0 && method.return_type != 0 {
|
||||||
if typ := c.resolve_generic_type(method.return_type, method.generic_names,
|
if typ := c.resolve_generic_by_names(method.return_type, method.generic_names,
|
||||||
call_expr.generic_types)
|
call_expr.generic_types)
|
||||||
{
|
{
|
||||||
call_expr.return_type = typ
|
call_expr.return_type = typ
|
||||||
|
@ -1955,8 +1955,11 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
mut fields := rts.info.fields.clone()
|
mut fields := rts.info.fields.clone()
|
||||||
if rts.info.generic_types.len == generic_types.len {
|
if rts.info.generic_types.len == generic_types.len {
|
||||||
for i, _ in fields {
|
for i, _ in fields {
|
||||||
fields[i].typ = c.unwrap_generic_ex(fields[i].typ, rts.info.generic_types,
|
if t_typ := c.resolve_generic_by_types(fields[i].typ, rts.info.generic_types,
|
||||||
generic_types)
|
generic_types)
|
||||||
|
{
|
||||||
|
fields[i].typ = t_typ
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mut info := rts.info
|
mut info := rts.info
|
||||||
info.generic_types = []
|
info.generic_types = []
|
||||||
|
@ -2098,7 +2101,26 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if f.generic_names.len > 0 {
|
if f.generic_names.len > 0 {
|
||||||
continue
|
if param.typ.has_flag(.generic)
|
||||||
|
&& f.generic_names.len == call_expr.generic_types.len {
|
||||||
|
if unwrap_typ := c.resolve_generic_by_names(param.typ, f.generic_names,
|
||||||
|
call_expr.generic_types)
|
||||||
|
{
|
||||||
|
if (unwrap_typ.idx() == typ.idx())
|
||||||
|
|| (unwrap_typ.is_int() && typ.is_int())
|
||||||
|
|| (unwrap_typ.is_float() && typ.is_float()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
expected_sym := c.table.get_type_symbol(unwrap_typ)
|
||||||
|
got_sym := c.table.get_type_symbol(typ)
|
||||||
|
c.error('argument ${i + 1} got `$got_sym.name`, expected `$expected_sym.name`',
|
||||||
|
call_arg.pos)
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos)
|
c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos)
|
||||||
}
|
}
|
||||||
|
@ -2116,7 +2138,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
c.infer_fn_types(f, mut call_expr)
|
c.infer_fn_types(f, mut call_expr)
|
||||||
}
|
}
|
||||||
if call_expr.generic_types.len > 0 && f.return_type != 0 {
|
if call_expr.generic_types.len > 0 && f.return_type != 0 {
|
||||||
if typ := c.resolve_generic_type(f.return_type, f.generic_names, call_expr.generic_types) {
|
if typ := c.resolve_generic_by_names(f.return_type, f.generic_names, call_expr.generic_types) {
|
||||||
call_expr.return_type = typ
|
call_expr.return_type = typ
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
@ -3901,45 +3923,6 @@ pub fn (c &Checker) unwrap_generic(typ table.Type) table.Type {
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
|
|
||||||
// `unwrap_generic()` is used in generic_fn decl, `unwrap_generic_ex()` can be used not in generic_fn decl
|
|
||||||
// e.g. from_types: <T, B> to_types: <int, string>
|
|
||||||
pub fn (mut c Checker) unwrap_generic_ex(typ table.Type, from_types []table.Type, to_types []table.Type) table.Type {
|
|
||||||
sym := c.table.get_type_symbol(typ)
|
|
||||||
mut typ_ := typ
|
|
||||||
mut nr_dims := 0
|
|
||||||
if sym.kind == .array {
|
|
||||||
typ_, nr_dims = c.array_element_info(typ)
|
|
||||||
}
|
|
||||||
if from_types.len == to_types.len {
|
|
||||||
for j, gp in from_types {
|
|
||||||
if gp == typ_ {
|
|
||||||
if sym.kind == .array {
|
|
||||||
idx := c.table.find_or_register_array_with_dims(to_types[j], nr_dims)
|
|
||||||
return table.new_type(idx)
|
|
||||||
} else {
|
|
||||||
return to_types[j].derive(typ).clear_flag(.generic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return typ
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut c Checker) array_element_info(typ table.Type) (table.Type, int) {
|
|
||||||
mut typ_ := typ
|
|
||||||
mut dims := 0
|
|
||||||
for {
|
|
||||||
sym := c.table.get_type_symbol(typ_)
|
|
||||||
if sym.info is table.Array {
|
|
||||||
typ_ = sym.info.elem_type
|
|
||||||
dims++
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return typ_, dims
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO node must be mut
|
// TODO node must be mut
|
||||||
pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
||||||
c.expr_level++
|
c.expr_level++
|
||||||
|
|
27
vlib/v/checker/tests/generics_fn_called_arg_mismatch.out
Normal file
27
vlib/v/checker/tests/generics_fn_called_arg_mismatch.out
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv:6:15: error: argument 1 got `int literal`, expected `bool`
|
||||||
|
4 |
|
||||||
|
5 | fn main() {
|
||||||
|
6 | foo<bool>(1)
|
||||||
|
| ^
|
||||||
|
7 | foo<bool>(2.2)
|
||||||
|
8 | foo<string>(true)
|
||||||
|
vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv:7:15: error: argument 1 got `float literal`, expected `bool`
|
||||||
|
5 | fn main() {
|
||||||
|
6 | foo<bool>(1)
|
||||||
|
7 | foo<bool>(2.2)
|
||||||
|
| ~~~
|
||||||
|
8 | foo<string>(true)
|
||||||
|
9 | foo<int>('aaa')
|
||||||
|
vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv:8:17: error: argument 1 got `bool`, expected `string`
|
||||||
|
6 | foo<bool>(1)
|
||||||
|
7 | foo<bool>(2.2)
|
||||||
|
8 | foo<string>(true)
|
||||||
|
| ~~~~
|
||||||
|
9 | foo<int>('aaa')
|
||||||
|
10 | }
|
||||||
|
vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv:9:14: error: argument 1 got `string`, expected `int`
|
||||||
|
7 | foo<bool>(2.2)
|
||||||
|
8 | foo<string>(true)
|
||||||
|
9 | foo<int>('aaa')
|
||||||
|
| ~~~~~
|
||||||
|
10 | }
|
10
vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv
Normal file
10
vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
fn foo<T>(b T) {
|
||||||
|
println(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo<bool>(1)
|
||||||
|
foo<bool>(2.2)
|
||||||
|
foo<string>(true)
|
||||||
|
foo<int>('aaa')
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user