checker: check if generic values have the same types (#13718)
							parent
							
								
									dff39bac78
								
							
						
					
					
						commit
						8b072aa962
					
				| 
						 | 
				
			
			@ -980,6 +980,12 @@ pub fn (mytable &Table) type_to_code(t Type) string {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// clean type name from generics form. From Type<int> -> Type
 | 
			
		||||
pub fn (t &Table) clean_generics_type_str(typ Type) string {
 | 
			
		||||
	result := t.type_to_str(typ)
 | 
			
		||||
	return result.all_before('<')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// import_aliases is a map of imported symbol aliases 'module.Type' => 'Type'
 | 
			
		||||
pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]string) string {
 | 
			
		||||
	sym := t.sym(typ)
 | 
			
		||||
| 
						 | 
				
			
			@ -1222,6 +1228,19 @@ pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string
 | 
			
		|||
	return sb.str()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get the name of the complete quanlified name of the type
 | 
			
		||||
// without the generic parts.
 | 
			
		||||
pub fn (t &TypeSymbol) symbol_name_except_generic() string {
 | 
			
		||||
	// main.Abc<int>
 | 
			
		||||
	mut embed_name := t.name
 | 
			
		||||
	// remove generic part from name
 | 
			
		||||
	// main.Abc<int> => main.Abc
 | 
			
		||||
	if embed_name.contains('<') {
 | 
			
		||||
		embed_name = embed_name.all_before('<')
 | 
			
		||||
	}
 | 
			
		||||
	return embed_name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn (t &TypeSymbol) embed_name() string {
 | 
			
		||||
	// main.Abc<int> => Abc<int>
 | 
			
		||||
	mut embed_name := t.name.split('.').last()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -188,7 +188,39 @@ pub fn (mut c Checker) check_expected_call_arg(got ast.Type, expected_ ast.Type,
 | 
			
		|||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return error('cannot use `${c.table.type_to_str(got.clear_flag(.variadic))}` as `${c.table.type_to_str(expected.clear_flag(.variadic))}`')
 | 
			
		||||
 | 
			
		||||
	// Check on Generics types, there are some case where we have the following case
 | 
			
		||||
	// `&Type<int> == &Type<>`. This is a common case we are implementing a function
 | 
			
		||||
	// with generic parameters like `compare(bst Bst<T> node) {}`
 | 
			
		||||
	got_typ_sym := c.table.sym(got)
 | 
			
		||||
	got_typ_str := c.table.type_to_str(got.clear_flag(.variadic))
 | 
			
		||||
	expected_typ_sym := c.table.sym(expected_)
 | 
			
		||||
	expected_typ_str := c.table.type_to_str(expected.clear_flag(.variadic))
 | 
			
		||||
 | 
			
		||||
	if got_typ_sym.symbol_name_except_generic() == expected_typ_sym.symbol_name_except_generic() {
 | 
			
		||||
		// Check if we are making a comparison between two different types of
 | 
			
		||||
		// the same type like `Type<int> and &Type<>`
 | 
			
		||||
		if (got.is_ptr() != expected.is_ptr()) || !c.check_same_module(got, expected) {
 | 
			
		||||
			return error('cannot use `$got_typ_str` as `$expected_typ_str`')
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return error('cannot use `$got_typ_str` as `$expected_typ_str`')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// helper method to check if the type is of the same module.
 | 
			
		||||
// FIXME(vincenzopalazzo) This is a work around to the issue
 | 
			
		||||
// explained in the https://github.com/vlang/v/pull/13718#issuecomment-1074517800
 | 
			
		||||
fn (c Checker) check_same_module(got ast.Type, expected ast.Type) bool {
 | 
			
		||||
	clean_got_typ := c.table.clean_generics_type_str(got.clear_flag(.variadic)).all_before('<')
 | 
			
		||||
	clean_expected_typ := c.table.clean_generics_type_str(expected.clear_flag(.variadic)).all_before('<')
 | 
			
		||||
	if clean_got_typ == clean_expected_typ {
 | 
			
		||||
		return true
 | 
			
		||||
		// The following if confition should catch the bugs descripted in the issue
 | 
			
		||||
	} else if clean_expected_typ.all_after('.') == clean_got_typ.all_after('.') {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn (mut c Checker) check_basic(got ast.Type, expected ast.Type) bool {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1243,10 +1243,6 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
 | 
			
		|||
				final_arg_sym = c.table.sym(final_arg_typ)
 | 
			
		||||
			}
 | 
			
		||||
			if exp_arg_typ.has_flag(.generic) {
 | 
			
		||||
				if concrete_types.len == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if exp_utyp := c.table.resolve_generic_to_concrete(exp_arg_typ, method.generic_names,
 | 
			
		||||
					concrete_types)
 | 
			
		||||
				{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
vlib/v/checker/tests/generic_parameter_on_method.vv:15:15: error: cannot use `&Type<int>` as `Type<>` in argument 1 to `ContainerType<int>.contains`
 | 
			
		||||
   13 | fn main() {
 | 
			
		||||
   14 |     con := ContainerType<int>{typ: &Type<int>{0}}
 | 
			
		||||
   15 |     con.contains(con.typ)
 | 
			
		||||
      |                  ~~~~~~~
 | 
			
		||||
   16 |     println(con)
 | 
			
		||||
   17 | }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
struct Type<T> {
 | 
			
		||||
	value T
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ContainerType<T> {
 | 
			
		||||
	typ &Type<T>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (instance &ContainerType<T>) contains(typ Type<T>) {
 | 
			
		||||
	println(typ)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
	con := ContainerType<int>{typ: &Type<int>{0}}
 | 
			
		||||
	con.contains(con.typ)
 | 
			
		||||
	println(con)
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue