checker: check array.map/filter fn or anon_fn

pull/5352/head
yuyi 2020-06-11 19:14:07 +08:00 committed by GitHub
parent 8f3f27f286
commit a66753871d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 134 additions and 0 deletions

View File

@ -754,6 +754,37 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
return c.call_fn(call_expr) return c.call_fn(call_expr)
} }
fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_expr ast.CallExpr) {
elem_sym := c.table.get_type_symbol(elem_typ)
match call_expr.args[0].expr {
ast.AnonFn {
if it.decl.args.len > 1 {
c.error('function needs exactly 1 argument', call_expr.pos)
} else if is_map && (it.decl.return_type != elem_typ || it.decl.args[0].typ != elem_typ) {
c.error('type mismatch, should use `fn(a $elem_sym.name) $elem_sym.name {...}`', call_expr.pos)
} else if !is_map && (it.decl.return_type != table.bool_type || it.decl.args[0].typ != elem_typ) {
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', call_expr.pos)
}
}
ast.Ident {
if it.kind == .function {
func := c.table.find_fn(it.name) or {
c.error('$it.name is not exist', it.pos)
return
}
if func.args.len > 1 {
c.error('function needs exactly 1 argument', call_expr.pos)
} else if is_map && (func.return_type != elem_typ || func.args[0].typ != elem_typ) {
c.error('type mismatch, should use `fn(a $elem_sym.name) $elem_sym.name {...}`', call_expr.pos)
} else if !is_map && (func.return_type != table.bool_type || func.args[0].typ != elem_typ) {
c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', call_expr.pos)
}
}
}
else {}
}
}
pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
left_type := c.expr(call_expr.left) left_type := c.expr(call_expr.left)
is_generic := left_type == table.t_type is_generic := left_type == table.t_type
@ -764,10 +795,12 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
// FIXME: Argument count != 1 will break these // FIXME: Argument count != 1 will break these
if left_type_sym.kind == .array && method_name in ['filter', 'clone', 'repeat', 'reverse', if left_type_sym.kind == .array && method_name in ['filter', 'clone', 'repeat', 'reverse',
'map', 'slice'] { 'map', 'slice'] {
mut elem_typ := table.void_type
if method_name in ['filter', 'map'] { if method_name in ['filter', 'map'] {
array_info := left_type_sym.info as table.Array array_info := left_type_sym.info as table.Array
mut scope := c.file.scope.innermost(call_expr.pos.pos) mut scope := c.file.scope.innermost(call_expr.pos.pos)
scope.update_var_type('it', array_info.elem_type) scope.update_var_type('it', array_info.elem_type)
elem_typ = array_info.elem_type
} }
// map/filter are supposed to have 1 arg only // map/filter are supposed to have 1 arg only
mut arg_type := left_type mut arg_type := left_type
@ -777,6 +810,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
call_expr.return_type = left_type call_expr.return_type = left_type
call_expr.receiver_type = left_type call_expr.receiver_type = left_type
if method_name == 'map' { if method_name == 'map' {
// check fn
c.check_map_and_filter(true, elem_typ, call_expr)
arg_sym := c.table.get_type_symbol(arg_type) arg_sym := c.table.get_type_symbol(arg_type)
// FIXME: match expr failed for now // FIXME: match expr failed for now
mut ret_type := 0 mut ret_type := 0
@ -785,6 +820,9 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
else { ret_type = arg_type } else { ret_type = arg_type }
} }
call_expr.return_type = c.table.find_or_register_array(ret_type, 1, c.mod) call_expr.return_type = c.table.find_or_register_array(ret_type, 1, c.mod)
} else if method_name == 'filter' {
// check fn
c.check_map_and_filter(false, elem_typ, call_expr)
} else if method_name == 'clone' { } else if method_name == 'clone' {
// need to return `array_xxx` instead of `array` // need to return `array_xxx` instead of `array`
// in ['clone', 'str'] { // in ['clone', 'str'] {

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/array_filter_anon_fn_err_a.v:2:23: error: function needs exactly 1 argument
1 | fn main() {
2 | a := [1,2,3,4].filter(fn(a int, b int) bool { return a > 0 })
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 | println(a)
4 | }

View File

@ -0,0 +1,4 @@
fn main() {
a := [1,2,3,4].filter(fn(a int, b int) bool { return a > 0 })
println(a)
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/array_filter_anon_fn_err_b.v:2:23: error: type mismatch, should use `fn(a int) bool {...}`
1 | fn main() {
2 | a := [1,2,3,4].filter(fn(a string) bool { return a.len > 0 })
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 | println(a)
4 | }

View File

@ -0,0 +1,4 @@
fn main() {
a := [1,2,3,4].filter(fn(a string) bool { return a.len > 0 })
println(a)
}

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/array_filter_fn_err_a.v:5:23: error: function needs exactly 1 argument
3 | }
4 | fn main() {
5 | a := [1,2,3,4].filter(fil)
| ~~~~~
6 | println(a)
7 | }

View File

@ -0,0 +1,7 @@
fn fil(a int, b int) bool {
return a > 0
}
fn main() {
a := [1,2,3,4].filter(fil)
println(a)
}

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/array_filter_fn_err_b.v:5:23: error: type mismatch, should use `fn(a int) bool {...}`
3 | }
4 | fn main() {
5 | a := [1,2,3,4].filter(fil)
| ~~~~~
6 | println(a)
7 | }

View File

@ -0,0 +1,7 @@
fn fil(a string) bool {
return a.len > 0
}
fn main() {
a := [1,2,3,4].filter(fil)
println(a)
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/array_map_anon_fn_err_a.v:2:20: error: function needs exactly 1 argument
1 | fn main() {
2 | a := [1,2,3,4].map(fn(a int, b int) int {return a + b})
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 | println(a)
4 | }

View File

@ -0,0 +1,4 @@
fn main() {
a := [1,2,3,4].map(fn(a int, b int) int {return a + b})
println(a)
}

View File

@ -0,0 +1,6 @@
vlib/v/checker/tests/array_map_anon_fn_err_b.v:2:20: error: type mismatch, should use `fn(a int) int {...}`
1 | fn main() {
2 | a := [1,2,3,4].map(fn(a string) string { return a })
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3 | println(a)
4 | }

View File

@ -0,0 +1,4 @@
fn main() {
a := [1,2,3,4].map(fn(a string) string { return a })
println(a)
}

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/array_map_fn_err_a.v:5:20: error: function needs exactly 1 argument
3 | }
4 | fn main() {
5 | a := [1,2,3,4].map(add)
| ~~~~~
6 | println(a)
7 | }

View File

@ -0,0 +1,7 @@
fn add(a int, b int) int {
return a + b
}
fn main() {
a := [1,2,3,4].map(add)
println(a)
}

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/array_map_fn_err_b.v:5:20: error: type mismatch, should use `fn(a int) int {...}`
3 | }
4 | fn main() {
5 | a := [1,2,3,4].map(add)
| ~~~~~
6 | println(a)
7 | }

View File

@ -0,0 +1,7 @@
fn add(a string) string {
return a
}
fn main() {
a := [1,2,3,4].map(add)
println(a)
}