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