checker: stricter check for function type signatures
							parent
							
								
									bb6ab185c3
								
							
						
					
					
						commit
						db4a9d6b59
					
				| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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') {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue