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