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)
|
typ_sym := c.table.get_type_symbol(typ)
|
||||||
mut inter_sym := c.table.get_type_symbol(inter_typ)
|
mut inter_sym := c.table.get_type_symbol(inter_typ)
|
||||||
styp := c.table.type_to_str(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) {
|
if method := typ_sym.find_method(imethod.name) {
|
||||||
msg := c.table.is_same_method(imethod, method)
|
msg := c.table.is_same_method(imethod, method)
|
||||||
if msg.len > 0 {
|
if msg.len > 0 {
|
||||||
|
@ -5263,9 +5268,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||||
}
|
}
|
||||||
if node.is_method {
|
if node.is_method {
|
||||||
mut sym := c.table.get_type_symbol(node.receiver.typ)
|
mut sym := c.table.get_type_symbol(node.receiver.typ)
|
||||||
if sym.kind == .interface_ {
|
if sym.kind == .array && !c.is_builtin_mod && node.name == 'map' {
|
||||||
c.error('interfaces cannot be used as method receiver', node.receiver_pos)
|
|
||||||
} else if sym.kind == .array && !c.is_builtin_mod && node.name == 'map' {
|
|
||||||
// TODO `node.map in array_builtin_methods`
|
// TODO `node.map in array_builtin_methods`
|
||||||
c.error('method overrides built-in array method', node.pos)
|
c.error('method overrides built-in array method', node.pos)
|
||||||
} else if sym.kind == .sum_type && node.name == 'type_name' {
|
} 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*)')
|
g.writeln('(*($opt_base_typ*)')
|
||||||
}
|
}
|
||||||
if sym.kind == .interface_ {
|
if sym.kind == .interface_ {
|
||||||
g.write('*(')
|
g.write('(*(')
|
||||||
}
|
}
|
||||||
if sym.kind == .array_fixed {
|
if sym.kind == .array_fixed {
|
||||||
assert node.field_name == 'len'
|
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)')
|
g.write('$sum_type_dot$sum_type_deref_field)')
|
||||||
}
|
}
|
||||||
if sym.kind == .interface_ {
|
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 {')
|
methods_struct_def.writeln('$methods_struct_name {')
|
||||||
mut imethods := map[string]string{} // a map from speak -> _Speaker_speak_fn
|
mut imethods := map[string]string{} // a map from speak -> _Speaker_speak_fn
|
||||||
mut methodidx := map[string]int{}
|
mut methodidx := map[string]int{}
|
||||||
for k, method in ityp.methods {
|
for k, method in inter_info.methods {
|
||||||
methodidx[method.name] = k
|
methodidx[method.name] = k
|
||||||
typ_name := '_${interface_name}_${method.name}_fn'
|
typ_name := '_${interface_name}_${method.name}_fn'
|
||||||
ret_styp := g.typ(method.return_type)
|
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
|
// add line return after interface index declarations
|
||||||
sb.writeln('')
|
sb.writeln('')
|
||||||
if ityp.methods.len > 0 {
|
if inter_info.methods.len > 0 {
|
||||||
sb.writeln(methods_wrapper.str())
|
sb.writeln(methods_wrapper.str())
|
||||||
sb.writeln(methods_typ_def.str())
|
sb.writeln(methods_typ_def.str())
|
||||||
sb.writeln(methods_struct_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))
|
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(typ_sym.name)
|
||||||
mut receiver_type_name := util.no_dots(g.cc_type2(g.unwrap_generic(node.receiver_type)))
|
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)
|
// Speaker_name_table[s._interface_idx].speak(s._object)
|
||||||
$if debug_interface_method_call ? {
|
$if debug_interface_method_call ? {
|
||||||
eprintln('>>> interface typ_sym.name: $typ_sym.name | receiver_type_name: $receiver_type_name')
|
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)
|
typ := table.new_type(reg_idx)
|
||||||
mut ts := p.table.get_type_symbol(typ)
|
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
|
// if methods were declared before, it's an error, ignore them
|
||||||
ts.methods = []table.Fn{cap: 20}
|
ts.methods = []table.Fn{cap: 20}
|
||||||
// Parse fields or methods
|
// Parse fields or methods
|
||||||
|
@ -470,7 +471,6 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
||||||
is_mut = true
|
is_mut = true
|
||||||
}
|
}
|
||||||
if p.peek_tok.kind == .lpar {
|
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()
|
method_start_pos := p.tok.position()
|
||||||
line_nr := p.tok.line_nr
|
line_nr := p.tok.line_nr
|
||||||
name := p.check_name()
|
name := p.check_name()
|
||||||
|
@ -511,13 +511,15 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
||||||
method.next_comments = mnext_comments
|
method.next_comments = mnext_comments
|
||||||
methods << method
|
methods << method
|
||||||
// println('register method $name')
|
// println('register method $name')
|
||||||
ts.register_method(
|
tmethod := table.Fn{
|
||||||
name: name
|
name: name
|
||||||
params: args
|
params: args
|
||||||
return_type: method.return_type
|
return_type: method.return_type
|
||||||
is_variadic: is_variadic
|
is_variadic: is_variadic
|
||||||
is_pub: true
|
is_pub: true
|
||||||
)
|
}
|
||||||
|
ts.register_method(tmethod)
|
||||||
|
info.methods << tmethod
|
||||||
} else {
|
} else {
|
||||||
// interface fields
|
// interface fields
|
||||||
field_pos := p.tok.position()
|
field_pos := p.tok.position()
|
||||||
|
@ -540,16 +542,15 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
||||||
comments: comments
|
comments: comments
|
||||||
is_public: true
|
is_public: true
|
||||||
}
|
}
|
||||||
mut info := ts.info as table.Interface
|
|
||||||
info.fields << table.Field{
|
info.fields << table.Field{
|
||||||
name: field_name
|
name: field_name
|
||||||
typ: field_typ
|
typ: field_typ
|
||||||
is_pub: true
|
is_pub: true
|
||||||
is_mut: is_mut
|
is_mut: is_mut
|
||||||
}
|
}
|
||||||
ts.info = info
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ts.info = info
|
||||||
p.top_level_statement_end()
|
p.top_level_statement_end()
|
||||||
p.check(.rcbr)
|
p.check(.rcbr)
|
||||||
pos.update_last_line(p.prev_tok.line_nr)
|
pos.update_last_line(p.prev_tok.line_nr)
|
||||||
|
|
|
@ -656,8 +656,9 @@ pub mut:
|
||||||
|
|
||||||
pub struct Interface {
|
pub struct Interface {
|
||||||
pub mut:
|
pub mut:
|
||||||
types []Type
|
types []Type
|
||||||
fields []Field
|
fields []Field
|
||||||
|
methods []Fn
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Enum {
|
pub struct Enum {
|
||||||
|
@ -991,3 +992,12 @@ pub fn (s Struct) get_field(name string) Field {
|
||||||
}
|
}
|
||||||
panic('unknown field `$name`')
|
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