checker: add mutability checks for interface fields; add tests (#8312)

pull/8329/head
spaceface 2021-01-24 22:11:17 +01:00 committed by GitHub
parent 815104e5d0
commit 4be45e8d02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 100 additions and 11 deletions

View File

@ -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
}

View File

@ -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 |

View File

@ -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)
}

View File

@ -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 | }

View File

@ -0,0 +1,10 @@
interface Animal {
name string
}
struct Cat {}
fn main() {
mut animals := []Animal{}
animals << Cat{}
}

View File

@ -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 | }

View File

@ -0,0 +1,12 @@
interface Animal {
name string
}
struct Cat {
name int
}
fn main() {
mut animals := []Animal{}
animals << Cat{}
}

View File

@ -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 | }

View File

@ -0,0 +1,13 @@
interface Animal {
mut:
name string
}
struct Cat {
name string
}
fn main() {
mut animals := []Animal{}
animals << Cat{}
}

View File

@ -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
}

View File

@ -1,4 +1,5 @@
interface Animal {
mut:
breed string
}