checker: don't disallow defining methods on interfaces (#8335)
							parent
							
								
									3959ba5751
								
							
						
					
					
						commit
						5f2b2df546
					
				| 
						 | 
				
			
			@ -1879,7 +1879,12 @@ fn (mut c Checker) type_implements(typ table.Type, inter_typ table.Type, pos tok
 | 
			
		|||
	typ_sym := c.table.get_type_symbol(typ)
 | 
			
		||||
	mut inter_sym := c.table.get_type_symbol(inter_typ)
 | 
			
		||||
	styp := c.table.type_to_str(typ)
 | 
			
		||||
	for imethod in inter_sym.methods {
 | 
			
		||||
	imethods := if inter_sym.kind == .interface_ {
 | 
			
		||||
		(inter_sym.info as table.Interface).methods
 | 
			
		||||
	} else {
 | 
			
		||||
		inter_sym.methods
 | 
			
		||||
	}
 | 
			
		||||
	for imethod in imethods {
 | 
			
		||||
		if method := typ_sym.find_method(imethod.name) {
 | 
			
		||||
			msg := c.table.is_same_method(imethod, method)
 | 
			
		||||
			if msg.len > 0 {
 | 
			
		||||
| 
						 | 
				
			
			@ -5263,9 +5268,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
 | 
			
		|||
	}
 | 
			
		||||
	if node.is_method {
 | 
			
		||||
		mut sym := c.table.get_type_symbol(node.receiver.typ)
 | 
			
		||||
		if sym.kind == .interface_ {
 | 
			
		||||
			c.error('interfaces cannot be used as method receiver', node.receiver_pos)
 | 
			
		||||
		} else if sym.kind == .array && !c.is_builtin_mod && node.name == 'map' {
 | 
			
		||||
		if sym.kind == .array && !c.is_builtin_mod && node.name == 'map' {
 | 
			
		||||
			// TODO `node.map in array_builtin_methods`
 | 
			
		||||
			c.error('method overrides built-in array method', node.pos)
 | 
			
		||||
		} else if sym.kind == .sum_type && node.name == 'type_name' {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +0,0 @@
 | 
			
		|||
vlib/v/checker/tests/no_interface_receiver.vv:5:5: error: interfaces cannot be used as method receiver
 | 
			
		||||
    3 | }
 | 
			
		||||
    4 |
 | 
			
		||||
    5 | fn (a Animal) str() {}
 | 
			
		||||
      |     ~~~~~~~~
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +0,0 @@
 | 
			
		|||
interface Animal {
 | 
			
		||||
	speak()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (a Animal) str() {}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +0,0 @@
 | 
			
		|||
vlib/v/checker/tests/no_interface_receiver_duplicate_a.vv:5:5: error: interfaces cannot be used as method receiver
 | 
			
		||||
    3 | }
 | 
			
		||||
    4 |
 | 
			
		||||
    5 | fn (a Abc) fun() {
 | 
			
		||||
      |     ~~~~~
 | 
			
		||||
    6 | }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +0,0 @@
 | 
			
		|||
interface Abc {
 | 
			
		||||
	fun()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (a Abc) fun() {
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +0,0 @@
 | 
			
		|||
vlib/v/checker/tests/no_interface_receiver_duplicate_b.vv:1:5: error: interfaces cannot be used as method receiver
 | 
			
		||||
    1 | fn (a Abc) fun() {
 | 
			
		||||
      |     ~~~~~
 | 
			
		||||
    2 | }
 | 
			
		||||
    3 |
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +0,0 @@
 | 
			
		|||
fn (a Abc) fun() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Abc {
 | 
			
		||||
	fun()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
vlib/v/checker/tests/no_method_on_interface_propagation.vv:18:5: error: unknown method: `Cat.foo`
 | 
			
		||||
   16 |     mut a := new_animal('persian')
 | 
			
		||||
   17 |     if a is Cat {
 | 
			
		||||
   18 |         a.foo()
 | 
			
		||||
      |           ~~~~~
 | 
			
		||||
   19 |     }
 | 
			
		||||
   20 | }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
struct Cat {
 | 
			
		||||
	breed string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Animal {
 | 
			
		||||
	breed string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (a Animal) foo() {}
 | 
			
		||||
 | 
			
		||||
fn new_animal(breed string) Animal {
 | 
			
		||||
	return &Cat{breed}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_methods_on_interfaces_dont_exist_on_implementers() {
 | 
			
		||||
	mut a := new_animal('persian')
 | 
			
		||||
	if a is Cat {
 | 
			
		||||
		a.foo()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2923,7 +2923,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
 | 
			
		|||
		g.writeln('(*($opt_base_typ*)')
 | 
			
		||||
	}
 | 
			
		||||
	if sym.kind == .interface_ {
 | 
			
		||||
		g.write('*(')
 | 
			
		||||
		g.write('(*(')
 | 
			
		||||
	}
 | 
			
		||||
	if sym.kind == .array_fixed {
 | 
			
		||||
		assert node.field_name == 'len'
 | 
			
		||||
| 
						 | 
				
			
			@ -3002,7 +3002,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
 | 
			
		|||
		g.write('$sum_type_dot$sum_type_deref_field)')
 | 
			
		||||
	}
 | 
			
		||||
	if sym.kind == .interface_ {
 | 
			
		||||
		g.write(')')
 | 
			
		||||
		g.write('))')
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5903,7 +5903,7 @@ fn (mut g Gen) interface_table() string {
 | 
			
		|||
		methods_struct_def.writeln('$methods_struct_name {')
 | 
			
		||||
		mut imethods := map[string]string{} // a map from speak -> _Speaker_speak_fn
 | 
			
		||||
		mut methodidx := map[string]int{}
 | 
			
		||||
		for k, method in ityp.methods {
 | 
			
		||||
		for k, method in inter_info.methods {
 | 
			
		||||
			methodidx[method.name] = k
 | 
			
		||||
			typ_name := '_${interface_name}_${method.name}_fn'
 | 
			
		||||
			ret_styp := g.typ(method.return_type)
 | 
			
		||||
| 
						 | 
				
			
			@ -6049,7 +6049,7 @@ $staticprefix $interface_name* I_${cctype}_to_Interface_${interface_name}_ptr($c
 | 
			
		|||
		}
 | 
			
		||||
		// add line return after interface index declarations
 | 
			
		||||
		sb.writeln('')
 | 
			
		||||
		if ityp.methods.len > 0 {
 | 
			
		||||
		if inter_info.methods.len > 0 {
 | 
			
		||||
			sb.writeln(methods_wrapper.str())
 | 
			
		||||
			sb.writeln(methods_typ_def.str())
 | 
			
		||||
			sb.writeln(methods_struct_def.str())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -343,7 +343,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
 | 
			
		|||
	typ_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver_type))
 | 
			
		||||
	// mut receiver_type_name := util.no_dots(typ_sym.name)
 | 
			
		||||
	mut receiver_type_name := util.no_dots(g.cc_type2(g.unwrap_generic(node.receiver_type)))
 | 
			
		||||
	if typ_sym.kind == .interface_ {
 | 
			
		||||
	if typ_sym.kind == .interface_ && (typ_sym.info as table.Interface).defines_method(node.name) {
 | 
			
		||||
		// Speaker_name_table[s._interface_idx].speak(s._object)
 | 
			
		||||
		$if debug_interface_method_call ? {
 | 
			
		||||
			eprintln('>>> interface typ_sym.name: $typ_sym.name | receiver_type_name: $receiver_type_name')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -453,6 +453,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
 | 
			
		|||
	}
 | 
			
		||||
	typ := table.new_type(reg_idx)
 | 
			
		||||
	mut ts := p.table.get_type_symbol(typ)
 | 
			
		||||
	mut info := ts.info as table.Interface
 | 
			
		||||
	// if methods were declared before, it's an error, ignore them
 | 
			
		||||
	ts.methods = []table.Fn{cap: 20}
 | 
			
		||||
	// Parse fields or methods
 | 
			
		||||
| 
						 | 
				
			
			@ -470,7 +471,6 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
 | 
			
		|||
			is_mut = true
 | 
			
		||||
		}
 | 
			
		||||
		if p.peek_tok.kind == .lpar {
 | 
			
		||||
			ts = p.table.get_type_symbol(typ) // removing causes memory bug visible by `v -silent test-fmt`
 | 
			
		||||
			method_start_pos := p.tok.position()
 | 
			
		||||
			line_nr := p.tok.line_nr
 | 
			
		||||
			name := p.check_name()
 | 
			
		||||
| 
						 | 
				
			
			@ -511,13 +511,15 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
 | 
			
		|||
			method.next_comments = mnext_comments
 | 
			
		||||
			methods << method
 | 
			
		||||
			// println('register method $name')
 | 
			
		||||
			ts.register_method(
 | 
			
		||||
			tmethod := table.Fn{
 | 
			
		||||
				name: name
 | 
			
		||||
				params: args
 | 
			
		||||
				return_type: method.return_type
 | 
			
		||||
				is_variadic: is_variadic
 | 
			
		||||
				is_pub: true
 | 
			
		||||
			)
 | 
			
		||||
			}
 | 
			
		||||
			ts.register_method(tmethod)
 | 
			
		||||
			info.methods << tmethod
 | 
			
		||||
		} else {
 | 
			
		||||
			// interface fields
 | 
			
		||||
			field_pos := p.tok.position()
 | 
			
		||||
| 
						 | 
				
			
			@ -540,16 +542,15 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
 | 
			
		|||
				comments: comments
 | 
			
		||||
				is_public: true
 | 
			
		||||
			}
 | 
			
		||||
			mut info := ts.info as table.Interface
 | 
			
		||||
			info.fields << table.Field{
 | 
			
		||||
				name: field_name
 | 
			
		||||
				typ: field_typ
 | 
			
		||||
				is_pub: true
 | 
			
		||||
				is_mut: is_mut
 | 
			
		||||
			}
 | 
			
		||||
			ts.info = info
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ts.info = info
 | 
			
		||||
	p.top_level_statement_end()
 | 
			
		||||
	p.check(.rcbr)
 | 
			
		||||
	pos.update_last_line(p.prev_tok.line_nr)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -656,8 +656,9 @@ pub mut:
 | 
			
		|||
 | 
			
		||||
pub struct Interface {
 | 
			
		||||
pub mut:
 | 
			
		||||
	types  []Type
 | 
			
		||||
	fields []Field
 | 
			
		||||
	types   []Type
 | 
			
		||||
	fields  []Field
 | 
			
		||||
	methods []Fn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Enum {
 | 
			
		||||
| 
						 | 
				
			
			@ -991,3 +992,12 @@ pub fn (s Struct) get_field(name string) Field {
 | 
			
		|||
	}
 | 
			
		||||
	panic('unknown field `$name`')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn (i Interface) defines_method(name string) bool {
 | 
			
		||||
	for method in i.methods {
 | 
			
		||||
		if method.name == name {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
struct Cat {
 | 
			
		||||
	breed string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Animal {
 | 
			
		||||
	breed string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (a Animal) info() string {
 | 
			
		||||
	return "I'm a ${a.breed} ${typeof(a).name}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn new_animal(breed string) Animal {
 | 
			
		||||
	return &Cat{ breed }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_methods_on_interfaces() {
 | 
			
		||||
	mut a := new_animal('persian')
 | 
			
		||||
	assert a.info() == "I'm a persian Animal"
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue