Skip to content

Commit

Permalink
checker: check arguments mismatch of array.sorted_with_compare() (fix #…
Browse files Browse the repository at this point in the history
  • Loading branch information
yuyi98 authored Sep 27, 2024
1 parent 2afce42 commit 372a402
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 41 deletions.
4 changes: 2 additions & 2 deletions vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ const generic_fn_postprocess_iterations_cutoff_limit = 1000_000
// are properly checked.
// Note that methods that do not return anything, or that return known types, are not listed here, since they are just ordinary non generic methods.
pub const array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort',
'sorted', 'sorted_with_compare', 'contains', 'index', 'wait', 'any', 'all', 'first', 'last',
'pop', 'delete']
'sort_with_compare', 'sorted', 'sorted_with_compare', 'contains', 'index', 'wait', 'any', 'all',
'first', 'last', 'pop', 'delete']
pub const array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(array_builtin_methods)
// TODO: remove `byte` from this list when it is no longer supported
pub const reserved_type_names = ['byte', 'bool', 'char', 'i8', 'i16', 'int', 'i64', 'u8', 'u16',
Expand Down
72 changes: 46 additions & 26 deletions vlib/v/checker/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -2549,27 +2549,6 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
}
}
}
if left_sym.info is ast.Array && method_name in ['sort_with_compare', 'sorted_with_compare'] {
elem_typ := left_sym.info.elem_type
arg_sym := c.table.sym(arg.typ)
if arg_sym.kind == .function {
func_info := arg_sym.info as ast.FnType
if func_info.func.params.len == 2 {
if func_info.func.params[0].typ.nr_muls() != elem_typ.nr_muls() + 1 {
arg_typ_str := c.table.type_to_str(func_info.func.params[0].typ)
expected_typ_str := c.table.type_to_str(elem_typ.ref())
c.error('${method_name} callback function parameter `${func_info.func.params[0].name}` with type `${arg_typ_str}` should be `${expected_typ_str}`',
func_info.func.params[0].type_pos)
}
if func_info.func.params[1].typ.nr_muls() != elem_typ.nr_muls() + 1 {
arg_typ_str := c.table.type_to_str(func_info.func.params[1].typ)
expected_typ_str := c.table.type_to_str(elem_typ.ref())
c.error('${method_name} callback function parameter `${func_info.func.params[1].name}` with type `${arg_typ_str}` should be `${expected_typ_str}`',
func_info.func.params[1].type_pos)
}
}
}
}
// Handle expected interface
if final_arg_sym.kind == .interface_ {
if c.type_implements(got_arg_typ, final_arg_typ, arg.expr.pos()) {
Expand Down Expand Up @@ -3184,9 +3163,48 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
// position of `it` doesn't matter
scope_register_it(mut node.scope, node.pos, elem_typ)
}
} else if node.args.len == 1 && method_name == 'sorted_with_compare' {
if node.args.len > 0 && mut node.args[0].expr is ast.LambdaExpr {
c.support_lambda_expr_in_sort(elem_typ.ref(), ast.int_type, mut node.args[0].expr)
} else if method_name in ['sort_with_compare', 'sorted_with_compare'] {
if node.args.len != 1 {
c.error('`.${method_name}()` expected 1 argument, but got ${node.args.len}',
node.pos)
} else {
if mut node.args[0].expr is ast.LambdaExpr {
c.support_lambda_expr_in_sort(elem_typ.ref(), ast.int_type, mut node.args[0].expr)
}
arg_type := c.expr(mut node.args[0].expr)
arg_sym := c.table.sym(arg_type)
if arg_sym.kind == .function {
func_info := arg_sym.info as ast.FnType
if func_info.func.params.len == 2 {
if func_info.func.params[0].typ.nr_muls() != elem_typ.nr_muls() + 1 {
arg_typ_str := c.table.type_to_str(func_info.func.params[0].typ)
expected_typ_str := c.table.type_to_str(elem_typ.ref())
c.error('${method_name} callback function parameter `${func_info.func.params[0].name}` with type `${arg_typ_str}` should be `${expected_typ_str}`',
func_info.func.params[0].type_pos)
}
if func_info.func.params[1].typ.nr_muls() != elem_typ.nr_muls() + 1 {
arg_typ_str := c.table.type_to_str(func_info.func.params[1].typ)
expected_typ_str := c.table.type_to_str(elem_typ.ref())
c.error('${method_name} callback function parameter `${func_info.func.params[1].name}` with type `${arg_typ_str}` should be `${expected_typ_str}`',
func_info.func.params[1].type_pos)
}
}
}
node.args[0].typ = arg_type
if method := c.table.find_method(left_sym, method_name) {
c.check_expected_call_arg(arg_type, method.params[1].typ, node.language,
node.args[0]) or {
c.error('${err.msg()} in argument 1 to `${left_sym.name}.${method_name}`',
node.args[0].pos)
}
}
if method_name == 'sort_with_compare' {
node.return_type = ast.void_type
node.receiver_type = node.left_type.ref()
} else {
node.return_type = node.left_type
node.receiver_type = node.left_type
}
}
} else if method_name == 'sort' || method_name == 'sorted' {
if method_name == 'sort' {
Expand Down Expand Up @@ -3300,8 +3318,10 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
c.ensure_same_array_return_type(mut node, left_type)
} else if method_name == 'sorted' {
c.ensure_same_array_return_type(mut node, left_type)
} else if method_name == 'sorted_with_compare' {
c.ensure_same_array_return_type(mut node, left_type)
} else if method_name in ['sort_with_compare', 'sorted_with_compare'] {
if method_name == 'sorted_with_compare' {
c.ensure_same_array_return_type(mut node, left_type)
}
// Inject a (voidptr) cast for the callback argument, to pass -cstrict, otherwise:
// error: incompatible function pointer types passing
// 'int (string *, string *)' (aka 'int (struct string *, struct string *)')
Expand Down
33 changes: 20 additions & 13 deletions vlib/v/checker/tests/array_sort_with_compare_err.out
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
vlib/v/checker/tests/array_sort_with_compare_err.vv:11:24: error: sort_with_compare callback function parameter `a` with type `string` should be `&string`
9 | }
10 |
11 | fn sort_by_file_base(a string, b string) int {
vlib/v/checker/tests/array_sort_with_compare_err.vv:13:24: error: sort_with_compare callback function parameter `a` with type `string` should be `&string`
11 | }
12 |
13 | fn sort_by_file_base(a string, b string) int {
| ~~~~~~
12 | return int(a > b)
13 | }
vlib/v/checker/tests/array_sort_with_compare_err.vv:11:34: error: sort_with_compare callback function parameter `b` with type `string` should be `&string`
9 | }
10 |
11 | fn sort_by_file_base(a string, b string) int {
14 | return int(a > b)
15 | }
vlib/v/checker/tests/array_sort_with_compare_err.vv:13:34: error: sort_with_compare callback function parameter `b` with type `string` should be `&string`
11 | }
12 |
13 | fn sort_by_file_base(a string, b string) int {
| ~~~~~~
12 | return int(a > b)
13 | }
14 | return int(a > b)
15 | }
vlib/v/checker/tests/array_sort_with_compare_err.vv:4:26: error: cannot use `fn (string, string) int` as `fn (voidptr, voidptr) int` in argument 1 to `[]string.sort_with_compare`
2 | mut names := ['aaa', 'bbb', 'ccc']
3 |
Expand All @@ -26,4 +26,11 @@ vlib/v/checker/tests/array_sort_with_compare_err.vv:7:26: error: cannot use `int
7 | names.sort_with_compare(22)
| ~~
8 | println(names)
9 | }
9 |
vlib/v/checker/tests/array_sort_with_compare_err.vv:10:8: error: `.sort_with_compare()` expected 1 argument, but got 2
8 | println(names)
9 |
10 | names.sort_with_compare(sort_by_file_base, 22)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 | }
12 |
2 changes: 2 additions & 0 deletions vlib/v/checker/tests/array_sort_with_compare_err.vv
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ fn main() {

names.sort_with_compare(22)
println(names)

names.sort_with_compare(sort_by_file_base, 22)
}

fn sort_by_file_base(a string, b string) int {
Expand Down
36 changes: 36 additions & 0 deletions vlib/v/checker/tests/array_sorted_with_compare_err.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
vlib/v/checker/tests/array_sorted_with_compare_err.vv:15:24: error: sorted_with_compare callback function parameter `a` with type `string` should be `&string`
13 | }
14 |
15 | fn sort_by_file_base(a string, b string) int {
| ~~~~~~
16 | return int(a > b)
17 | }
vlib/v/checker/tests/array_sorted_with_compare_err.vv:15:34: error: sorted_with_compare callback function parameter `b` with type `string` should be `&string`
13 | }
14 |
15 | fn sort_by_file_base(a string, b string) int {
| ~~~~~~
16 | return int(a > b)
17 | }
vlib/v/checker/tests/array_sorted_with_compare_err.vv:4:37: error: cannot use `fn (string, string) int` as `fn (voidptr, voidptr) int` in argument 1 to `[]string.sorted_with_compare`
2 | names := ['aaa', 'bbb', 'ccc']
3 |
4 | name1 := names.sorted_with_compare(sort_by_file_base)
| ~~~~~~~~~~~~~~~~~
5 | println(name1)
6 |
Details: expected argument 1 to be a pointer, but the passed argument 1 is NOT a pointer
vlib/v/checker/tests/array_sorted_with_compare_err.vv:7:37: error: cannot use `int literal` as `fn (voidptr, voidptr) int` in argument 1 to `[]string.sorted_with_compare`
5 | println(name1)
6 |
7 | name2 := names.sorted_with_compare(22)
| ~~
8 | println(name2)
9 |
vlib/v/checker/tests/array_sorted_with_compare_err.vv:10:17: error: `.sorted_with_compare()` expected 1 argument, but got 2
8 | println(name2)
9 |
10 | name3 := names.sorted_with_compare(sort_by_file_base, 22)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11 | println(name3)
12 |
17 changes: 17 additions & 0 deletions vlib/v/checker/tests/array_sorted_with_compare_err.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
fn main() {
names := ['aaa', 'bbb', 'ccc']

name1 := names.sorted_with_compare(sort_by_file_base)
println(name1)

name2 := names.sorted_with_compare(22)
println(name2)

name3 := names.sorted_with_compare(sort_by_file_base, 22)
println(name3)

}

fn sort_by_file_base(a string, b string) int {
return int(a > b)
}

0 comments on commit 372a402

Please sign in to comment.