checker: stricter check for function type signatures

pull/5669/head
Delyan Angelov 2020-07-04 20:58:10 +03:00
parent bb6ab185c3
commit db4a9d6b59
3 changed files with 44 additions and 16 deletions

View File

@ -8,6 +8,9 @@ import v.token
import v.ast
pub fn (c &Checker) check_basic(got, expected table.Type) bool {
if got == expected {
return true
}
t := c.table
got_idx := t.unalias_num_type(got).idx()
exp_idx := t.unalias_num_type(expected).idx()
@ -117,25 +120,43 @@ pub fn (c &Checker) check_basic(got, expected table.Type) bool {
}
// fn type
if got_type_sym.kind == .function && exp_type_sym.kind == .function {
got_info := got_type_sym.info as table.FnType
exp_info := exp_type_sym.info as table.FnType
got_fn := got_info.func
exp_fn := exp_info.func
// we are using check() to compare return type & args as they might include
// functions themselves. TODO: optimize, only use check() when needed
if got_fn.args.len == exp_fn.args.len && c.check_basic(got_fn.return_type, exp_fn.return_type) {
for i, got_arg in got_fn.args {
exp_arg := exp_fn.args[i]
if !c.check_basic(got_arg.typ, exp_arg.typ) {
return false
}
}
return true
}
return c.check_matching_function_symbols(got_type_sym, exp_type_sym)
}
return false
}
pub fn (c &Checker) check_matching_function_symbols(got_type_sym &table.TypeSymbol, exp_type_sym &table.TypeSymbol) bool {
got_info := got_type_sym.info as table.FnType
exp_info := exp_type_sym.info as table.FnType
got_fn := got_info.func
exp_fn := exp_info.func
// we are using check() to compare return type & args as they might include
// functions themselves. TODO: optimize, only use check() when needed
if got_fn.args.len != exp_fn.args.len {
return false
}
if !c.check_basic(got_fn.return_type, exp_fn.return_type) {
return false
}
for i, got_arg in got_fn.args {
exp_arg := exp_fn.args[i]
exp_arg_is_ptr := exp_arg.typ.is_ptr() || exp_arg.typ.is_pointer()
got_arg_is_ptr := got_arg.typ.is_ptr() || got_arg.typ.is_pointer()
if exp_arg_is_ptr != got_arg_is_ptr {
$if debug_matching_function_symbols ? {
exp_arg_pointedness := if exp_arg_is_ptr { 'a pointer' } else { 'NOT a pointer' }
got_arg_pointedness := if got_arg_is_ptr { 'a pointer' } else { 'NOT a pointer' }
eprintln('`$exp_fn.name` expected fn argument: `$exp_arg.name` is $exp_arg_pointedness, but `$got_fn.name` actual fn argument: `$got_arg.name` is $got_arg_pointedness')
}
return false
}
if !c.check_basic(got_arg.typ, exp_arg.typ) {
return false
}
}
return true
}
[inline]
fn (c &Checker) check_shift(left_type, right_type table.Type, left_pos, right_pos token.Position) table.Type {
if !left_type.is_int() {
@ -215,6 +236,9 @@ fn (c &Checker) promote_num(left_type, right_type table.Type) table.Type {
// TODO: promote(), check_types(), symmetric_check() and check() overlap - should be rearranged
pub fn (c &Checker) check_types(got, expected table.Type) bool {
if got == expected {
return true
}
exp_idx := expected.idx()
got_idx := got.idx()
if exp_idx == got_idx {

View File

@ -1180,6 +1180,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
return true
}
*/
if !c.check_types(typ, arg.typ) {
// str method, allow type with str method if fn arg is string
if arg_typ_sym.kind == .string && typ_sym.has_method('str') {

View File

@ -641,7 +641,10 @@ pub fn (k Kind) str() string {
.alias { 'alias' }
.enum_ { 'enum' }
.any { 'any' }
else { 'unknown' }
.function { 'function' }
.interface_ { 'interface' }
.ustring { 'ustring' }
.generic_struct_inst { 'generic_struct_inst' }
}
return k_str
}