checker: add mutability checks for interface fields; add tests (#8312)
parent
815104e5d0
commit
4be45e8d02
|
@ -1147,13 +1147,18 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
|
|||
}
|
||||
}
|
||||
.interface_ {
|
||||
// TODO: mutability checks on interface fields?
|
||||
interface_info := typ_sym.info as table.Interface
|
||||
interface_info.find_field(expr.field_name) or {
|
||||
mut field_info := interface_info.find_field(expr.field_name) or {
|
||||
type_str := c.table.type_to_str(expr.expr_type)
|
||||
c.error('unknown field `${type_str}.$expr.field_name`', expr.pos)
|
||||
return '', pos
|
||||
}
|
||||
if !field_info.is_mut {
|
||||
type_str := c.table.type_to_str(expr.expr_type)
|
||||
c.error('field `$expr.field_name` of interface `$type_str` is immutable',
|
||||
expr.pos)
|
||||
}
|
||||
c.fail_if_immutable(expr.expr)
|
||||
}
|
||||
.array, .string {
|
||||
// This should only happen in `builtin`
|
||||
|
@ -1942,6 +1947,10 @@ fn (mut c Checker) type_implements(typ table.Type, inter_typ table.Type, pos tok
|
|||
c.error('`$styp` incorrectly implements field `$ifield.name` of interface `$inter_sym.name`, expected `$exp`, got `$got`',
|
||||
pos)
|
||||
return false
|
||||
} else if ifield.is_mut && !(field.is_mut || field.is_global) {
|
||||
c.error('`$styp` incorrectly implements interface `$inter_sym.name`, field `$ifield.name` must be mutable',
|
||||
pos)
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/immutable_interface_field.vv:10:4: error: field `i1` of interface `&Bbb` is immutable
|
||||
8 |
|
||||
9 | fn mutate_interface(mut b Bbb) {
|
||||
10 | b.i1 = 2
|
||||
| ~~
|
||||
11 | }
|
||||
12 |
|
|
@ -0,0 +1,16 @@
|
|||
struct Aaa {
|
||||
i1 int
|
||||
}
|
||||
|
||||
interface Bbb {
|
||||
i1 int
|
||||
}
|
||||
|
||||
fn mutate_interface(mut b Bbb) {
|
||||
b.i1 = 2
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut a := Aaa{1}
|
||||
mutate_interface(mut a)
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/unimplemented_interface_h.vv:9:13: error: `Cat` doesn't implement field `name` of interface `Animal`
|
||||
7 | fn main() {
|
||||
8 | mut animals := []Animal{}
|
||||
9 | animals << Cat{}
|
||||
| ~~~~~
|
||||
10 | }
|
|
@ -0,0 +1,10 @@
|
|||
interface Animal {
|
||||
name string
|
||||
}
|
||||
|
||||
struct Cat {}
|
||||
|
||||
fn main() {
|
||||
mut animals := []Animal{}
|
||||
animals << Cat{}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/unimplemented_interface_i.vv:11:13: error: `Cat` incorrectly implements field `name` of interface `Animal`, expected `string`, got `int`
|
||||
9 | fn main() {
|
||||
10 | mut animals := []Animal{}
|
||||
11 | animals << Cat{}
|
||||
| ~~~~~
|
||||
12 | }
|
|
@ -0,0 +1,12 @@
|
|||
interface Animal {
|
||||
name string
|
||||
}
|
||||
|
||||
struct Cat {
|
||||
name int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut animals := []Animal{}
|
||||
animals << Cat{}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/unimplemented_interface_j.vv:12:13: error: `Cat` incorrectly implements interface `Animal`, field `name` must be mutable
|
||||
10 | fn main() {
|
||||
11 | mut animals := []Animal{}
|
||||
12 | animals << Cat{}
|
||||
| ~~~~~
|
||||
13 | }
|
|
@ -0,0 +1,13 @@
|
|||
interface Animal {
|
||||
mut:
|
||||
name string
|
||||
}
|
||||
|
||||
struct Cat {
|
||||
name string
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut animals := []Animal{}
|
||||
animals << Cat{}
|
||||
}
|
|
@ -460,17 +460,17 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
mut methods := []ast.FnDecl{cap: 20}
|
||||
mut is_mut := false
|
||||
for p.tok.kind != .rcbr && p.tok.kind != .eof {
|
||||
if p.tok.kind == .key_mut {
|
||||
if is_mut {
|
||||
p.error_with_pos('redefinition of `mut` section', p.tok.position())
|
||||
return {}
|
||||
}
|
||||
p.next()
|
||||
p.check(.colon)
|
||||
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`
|
||||
if p.tok.kind == .key_mut {
|
||||
if is_mut {
|
||||
p.error_with_pos('redefinition of `mut` section', p.tok.position())
|
||||
return {}
|
||||
}
|
||||
p.next()
|
||||
p.check(.colon)
|
||||
is_mut = true
|
||||
}
|
||||
method_start_pos := p.tok.position()
|
||||
line_nr := p.tok.line_nr
|
||||
name := p.check_name()
|
||||
|
@ -538,11 +538,14 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
|
|||
type_pos: type_pos
|
||||
typ: field_typ
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
interface Animal {
|
||||
mut:
|
||||
breed string
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue