Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

checker: check arguments mismatch of array.sorted_with_compare() (fix #22327) #22328

Merged
merged 3 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
Loading