checker: extract into call_array_builtin_method to cleanup (#9225)

pull/9176/head^2
zakuro 2021-03-11 03:19:39 +09:00 committed by GitHub
parent ef73e07e62
commit f280a5c230
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 88 additions and 83 deletions

View File

@ -1315,89 +1315,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
// TODO: remove this for actual methods, use only for compiler magic
// FIXME: Argument count != 1 will break these
if left_type_sym.kind == .array && method_name in checker.array_builtin_methods {
mut elem_typ := table.void_type
is_filter_map := method_name in ['filter', 'map']
is_sort := method_name == 'sort'
is_slice := method_name == 'slice'
is_wait := method_name == 'wait'
if is_slice && !c.is_builtin_mod {
c.error('.slice() is a private method, use `x[start..end]` instead', call_expr.pos)
}
if is_filter_map || is_sort || is_wait {
array_info := left_type_sym.info as table.Array
if is_filter_map {
// position of `it` doesn't matter
scope_register_it(mut call_expr.scope, call_expr.pos, array_info.elem_type)
} else if is_sort {
c.fail_if_immutable(call_expr.left)
// position of `a` and `b` doesn't matter, they're the same
scope_register_ab(mut call_expr.scope, call_expr.pos, array_info.elem_type)
if call_expr.args.len > 1 {
c.error('expected 0 or 1 argument, but got $call_expr.args.len', call_expr.pos)
}
// Verify `.sort(a < b)`
if call_expr.args.len > 0 {
if call_expr.args[0].expr !is ast.InfixExpr {
c.error(
'`.sort()` requires a `<` or `>` comparison as the first and only argument' +
'\ne.g. `users.sort(a.id < b.id)`', call_expr.pos)
}
}
}
elem_typ = array_info.elem_type
if is_wait {
elem_sym := c.table.get_type_symbol(elem_typ)
if elem_sym.kind == .thread {
if call_expr.args.len != 0 {
c.error('`.wait()` does not have any arguments', call_expr.args[0].pos)
}
thread_ret_type := elem_sym.thread_info().return_type
if thread_ret_type.has_flag(.optional) {
c.error('`.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`.',
call_expr.pos)
}
call_expr.return_type = c.table.find_or_register_array(thread_ret_type)
} else {
c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)',
call_expr.left.position())
}
}
}
// map/filter are supposed to have 1 arg only
mut arg_type := left_type
for arg in call_expr.args {
arg_type = c.expr(arg.expr)
}
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)
ret_type := match arg_sym.info {
table.FnType { arg_sym.info.func.return_type }
else { arg_type }
}
call_expr.return_type = c.table.find_or_register_array(ret_type)
} else if method_name == 'filter' {
// check fn
c.check_map_and_filter(false, elem_typ, call_expr)
} else if method_name == 'clone' {
// need to return `array_xxx` instead of `array`
// in ['clone', 'str'] {
call_expr.receiver_type = left_type.to_ptr()
if call_expr.left.is_auto_deref_var() {
call_expr.return_type = left_type.deref()
} else {
call_expr.return_type = call_expr.receiver_type.set_nr_muls(0)
}
} else if method_name == 'sort' {
call_expr.return_type = table.void_type
} else if method_name == 'contains' {
call_expr.return_type = table.bool_type
} else if method_name == 'index' {
call_expr.return_type = table.int_type
}
return call_expr.return_type
return c.call_array_builtin_method(mut call_expr, left_type, left_type_sym)
} else if left_type_sym.kind == .map && method_name in ['clone', 'keys', 'move'] {
mut ret_type := table.void_type
match method_name {
@ -1696,6 +1614,93 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
return table.void_type
}
fn (mut c Checker) call_array_builtin_method(mut call_expr ast.CallExpr, left_type table.Type, left_type_sym table.TypeSymbol) table.Type {
method_name := call_expr.name
mut elem_typ := table.void_type
is_filter_map := method_name in ['filter', 'map']
is_sort := method_name == 'sort'
is_slice := method_name == 'slice'
is_wait := method_name == 'wait'
if is_slice && !c.is_builtin_mod {
c.error('.slice() is a private method, use `x[start..end]` instead', call_expr.pos)
}
if is_filter_map || is_sort || is_wait {
array_info := left_type_sym.info as table.Array
if is_filter_map {
// position of `it` doesn't matter
scope_register_it(mut call_expr.scope, call_expr.pos, array_info.elem_type)
} else if is_sort {
c.fail_if_immutable(call_expr.left)
// position of `a` and `b` doesn't matter, they're the same
scope_register_ab(mut call_expr.scope, call_expr.pos, array_info.elem_type)
if call_expr.args.len > 1 {
c.error('expected 0 or 1 argument, but got $call_expr.args.len', call_expr.pos)
}
// Verify `.sort(a < b)`
if call_expr.args.len > 0 {
if call_expr.args[0].expr !is ast.InfixExpr {
c.error(
'`.sort()` requires a `<` or `>` comparison as the first and only argument' +
'\ne.g. `users.sort(a.id < b.id)`', call_expr.pos)
}
}
}
elem_typ = array_info.elem_type
if is_wait {
elem_sym := c.table.get_type_symbol(elem_typ)
if elem_sym.kind == .thread {
if call_expr.args.len != 0 {
c.error('`.wait()` does not have any arguments', call_expr.args[0].pos)
}
thread_ret_type := elem_sym.thread_info().return_type
if thread_ret_type.has_flag(.optional) {
c.error('`.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`.',
call_expr.pos)
}
call_expr.return_type = c.table.find_or_register_array(thread_ret_type)
} else {
c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)',
call_expr.left.position())
}
}
}
// map/filter are supposed to have 1 arg only
mut arg_type := left_type
for arg in call_expr.args {
arg_type = c.expr(arg.expr)
}
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)
ret_type := match arg_sym.info {
table.FnType { arg_sym.info.func.return_type }
else { arg_type }
}
call_expr.return_type = c.table.find_or_register_array(ret_type)
} else if method_name == 'filter' {
// check fn
c.check_map_and_filter(false, elem_typ, call_expr)
} else if method_name == 'clone' {
// need to return `array_xxx` instead of `array`
// in ['clone', 'str'] {
call_expr.receiver_type = left_type.to_ptr()
if call_expr.left.is_auto_deref_var() {
call_expr.return_type = left_type.deref()
} else {
call_expr.return_type = call_expr.receiver_type.set_nr_muls(0)
}
} else if method_name == 'sort' {
call_expr.return_type = table.void_type
} else if method_name == 'contains' {
call_expr.return_type = table.bool_type
} else if method_name == 'index' {
call_expr.return_type = table.int_type
}
return call_expr.return_type
}
pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
fn_name := call_expr.name
if fn_name == 'main' {