checker: check the count of generics in fn arguments (#13855)

pull/13858/head
yuyi 2022-03-29 15:04:19 +08:00 committed by GitHub
parent 4f551d76c0
commit fd34ebd84e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 8 deletions

View File

@ -673,6 +673,12 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
&& !c.file.is_translated && !func.is_unsafe { && !c.file.is_translated && !func.is_unsafe {
c.error('cannot call a function that does not have a body', node.pos) c.error('cannot call a function that does not have a body', node.pos)
} }
if node.concrete_types.len > 0 && func.generic_names.len > 0
&& node.concrete_types.len != func.generic_names.len {
desc := if node.concrete_types.len > func.generic_names.len { 'many' } else { 'little' }
c.error('too $desc generic parameters got $node.concrete_types.len, expected $func.generic_names.len',
node.concrete_list_pos)
}
for concrete_type in node.concrete_types { for concrete_type in node.concrete_types {
c.ensure_type_exists(concrete_type, node.concrete_list_pos) or {} c.ensure_type_exists(concrete_type, node.concrete_list_pos) or {}
} }
@ -961,10 +967,6 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
c.error('a non generic function called like a generic one', node.concrete_list_pos) c.error('a non generic function called like a generic one', node.concrete_list_pos)
} }
if node.concrete_types.len > func.generic_names.len {
c.error('too many generic parameters got $node.concrete_types.len, expected $func.generic_names.len',
node.concrete_list_pos)
}
if func.generic_names.len > 0 { if func.generic_names.len > 0 {
if has_generic { if has_generic {
if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names, if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
@ -1189,6 +1191,19 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
&& method.language == .v && method.no_body { && method.language == .v && method.no_body {
c.error('cannot call a method that does not have a body', node.pos) c.error('cannot call a method that does not have a body', node.pos)
} }
if node.concrete_types.len > 0 && method.generic_names.len > 0
&& node.concrete_types.len != method.generic_names.len {
desc := if node.concrete_types.len > method.generic_names.len {
'many'
} else {
'little'
}
c.error('too $desc generic parameters got $node.concrete_types.len, expected $method.generic_names.len',
node.concrete_list_pos)
}
for concrete_type in node.concrete_types {
c.ensure_type_exists(concrete_type, node.concrete_list_pos) or {}
}
if method.return_type == ast.void_type && method.is_conditional if method.return_type == ast.void_type && method.is_conditional
&& method.ctdefine_idx != ast.invalid_type_idx { && method.ctdefine_idx != ast.invalid_type_idx {
node.should_be_skipped = c.evaluate_once_comptime_if_attribute(mut method.attrs[method.ctdefine_idx]) node.should_be_skipped = c.evaluate_once_comptime_if_attribute(mut method.attrs[method.ctdefine_idx])
@ -1395,10 +1410,6 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
if node.concrete_types.len > 0 && method.generic_names.len == 0 { if node.concrete_types.len > 0 && method.generic_names.len == 0 {
c.error('a non generic function called like a generic one', node.concrete_list_pos) c.error('a non generic function called like a generic one', node.concrete_list_pos)
} }
if node.concrete_types.len > method.generic_names.len {
c.error('too many generic parameters got $node.concrete_types.len, expected $method.generic_names.len',
node.concrete_list_pos)
}
if method.generic_names.len > 0 { if method.generic_names.len > 0 {
if !left_type.has_flag(.generic) { if !left_type.has_flag(.generic) {
if left_sym.info is ast.Struct { if left_sym.info is ast.Struct {

View File

@ -0,0 +1,41 @@
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:12:18: error: too little generic parameters got 1, expected 2
10 |
11 | fn main() {
12 | ret1 := get_name<int>(11, 22)
| ~~~~~
13 | println(ret1)
14 |
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:15:18: error: too many generic parameters got 3, expected 2
13 | println(ret1)
14 |
15 | ret2 := get_name<int, int, string>(11, 22, 'hello')
| ~~~~~~~~~~~~~~~~~~
16 | println(ret2)
17 |
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:19:22: error: too little generic parameters got 1, expected 2
17 |
18 | foo := Foo{}
19 | ret3 := foo.get_name<int>(11, 22)
| ~~~~~
20 | println(ret3)
21 |
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:22:22: error: too many generic parameters got 3, expected 2
20 | println(ret3)
21 |
22 | ret4 := foo.get_name<int, int, string>(11, 22, 'hello')
| ~~~~~~~~~~~~~~~~~~
23 | println(ret4)
24 | }
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:2:15: error: no known default format for type `B`
1 | fn get_name<A, B>(a A, b B) string {
2 | return '$a, $b'
| ^
3 | }
4 |
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:8:15: error: no known default format for type `B`
6 |
7 | fn (f Foo) get_name<A, B>(a A, b B) string {
8 | return '$a, $b'
| ^
9 | }
10 |

View File

@ -0,0 +1,24 @@
fn get_name<A, B>(a A, b B) string {
return '$a, $b'
}
struct Foo {}
fn (f Foo) get_name<A, B>(a A, b B) string {
return '$a, $b'
}
fn main() {
ret1 := get_name<int>(11, 22)
println(ret1)
ret2 := get_name<int, int, string>(11, 22, 'hello')
println(ret2)
foo := Foo{}
ret3 := foo.get_name<int>(11, 22)
println(ret3)
ret4 := foo.get_name<int, int, string>(11, 22, 'hello')
println(ret4)
}