checker/channels: check `mut`/`&` state of transmitted objects (#6315)

pull/6324/head
Uwe Krüger 2020-09-06 21:24:41 +02:00 committed by GitHub
parent 601d098b48
commit 2cb711ee15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 171 additions and 23 deletions

View File

@ -682,10 +682,19 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
}
.arrow { // `chan <- elem`
if left.kind == .chan {
elem_type := left.chan_info().elem_type
chan_info := left.chan_info()
elem_type := chan_info.elem_type
if !c.check_types(right_type, elem_type) {
c.error('cannot push `$right.name` on `$left.name`', right_pos)
}
if chan_info.is_mut {
// TODO: The error message of the following could be more specific...
c.fail_if_immutable(infix_expr.right)
}
if elem_type.is_ptr() && !right_type.is_ptr() {
c.error('cannon push non-reference `$right.source_name` on `$left.source_name`',
right_pos)
}
} else {
c.error('cannot push on non-channel `$left.name`', left_pos)
}
@ -1687,8 +1696,9 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
}
right_first := assign_stmt.right[0]
mut right_len := assign_stmt.right.len
mut right_type0 := table.void_type
if right_first is ast.CallExpr || right_first is ast.IfExpr || right_first is ast.MatchExpr {
right_type0 := c.expr(right_first)
right_type0 = c.expr(right_first)
assign_stmt.right_types = [
c.check_expr_opt_call(right_first, right_type0),
]
@ -1710,19 +1720,34 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
}
return
}
// Check `x := &y`
// Check `x := &y` and `mut x := <-ch`
if right_first is ast.PrefixExpr {
node := right_first
left_first := assign_stmt.left[0]
if node.op == .amp && node.right is ast.Ident {
ident := node.right as ast.Ident
scope := c.file.scope.innermost(node.pos.pos)
if v := scope.find_var(ident.name) {
if left_first is ast.Ident {
assigned_var := left_first
if !v.is_mut && assigned_var.is_mut && !c.inside_unsafe {
c.error('`$ident.name` is immutable, cannot have a mutable reference to it',
node.pos)
if left_first is ast.Ident {
assigned_var := left_first
if node.right is ast.Ident {
ident := node.right as ast.Ident
scope := c.file.scope.innermost(node.pos.pos)
if v := scope.find_var(ident.name) {
right_type0 = v.typ
if node.op == .amp {
if !v.is_mut && assigned_var.is_mut && !c.inside_unsafe {
c.error('`$ident.name` is immutable, cannot have a mutable reference to it',
node.pos)
}
}
}
}
if node.op == .arrow {
if assigned_var.is_mut {
right_sym := c.table.get_type_symbol(right_type0)
if right_sym.kind == .chan {
chan_info := right_sym.chan_info()
if chan_info.elem_type.is_ptr() && !chan_info.is_mut {
c.error('cannot have a mutable reference to object from `$right_sym.source_name`',
node.pos)
}
}
}
}

View File

@ -0,0 +1,28 @@
vlib/v/checker/tests/chan_mut.vv:8:8: error: `v` is immutable, declare it with `mut` to make it mutable
6 | fn f(ch chan mut St) {
7 | v := St{}
8 | ch <- v
| ^
9 | mut w := St{}
10 | ch <- w
vlib/v/checker/tests/chan_mut.vv:10:8: error: cannon push non-reference `St` on `chan mut St`
8 | ch <- v
9 | mut w := St{}
10 | ch <- w
| ^
11 | x := &St{}
12 | ch <- x
vlib/v/checker/tests/chan_mut.vv:12:8: error: `x` is immutable, declare it with `mut` to make it mutable
10 | ch <- w
11 | x := &St{}
12 | ch <- x
| ^
13 | mut y := St{}
14 | ch <- y
vlib/v/checker/tests/chan_mut.vv:14:8: error: cannon push non-reference `St` on `chan mut St`
12 | ch <- x
13 | mut y := St{}
14 | ch <- y
| ^
15 | mut z := &St{n: 7}
16 | // this works

View File

@ -0,0 +1,27 @@
struct St{
mut:
n int
}
fn f(ch chan mut St) {
v := St{}
ch <- v
mut w := St{}
ch <- w
x := &St{}
ch <- x
mut y := St{}
ch <- y
mut z := &St{n: 7}
// this works
ch <- z
}
fn main() {
c := chan mut St{}
go f(c)
mut y := <-c
z := <-c
println(y)
println(z)
}

View File

@ -0,0 +1,21 @@
vlib/v/checker/tests/chan_ref.vv:10:8: error: cannon push non-reference `St` on `chan &St`
8 | fn f(ch chan &St, sem sync.Semaphore) {
9 | w := St{}
10 | ch <- w
| ^
11 | mut x := St{}
12 | ch <- x
vlib/v/checker/tests/chan_ref.vv:12:8: error: cannon push non-reference `St` on `chan &St`
10 | ch <- w
11 | mut x := St{}
12 | ch <- x
| ^
13 | // the following works
14 | y := &St{}
vlib/v/checker/tests/chan_ref.vv:28:11: error: cannot have a mutable reference to object from `chan &St`
26 | y := <-c
27 | // this should fail
28 | mut z := <-c
| ~~
29 | z.n = 9
30 | sem.post()

View File

@ -0,0 +1,33 @@
import sync
struct St{
mut:
n int
}
fn f(ch chan &St, sem sync.Semaphore) {
w := St{}
ch <- w
mut x := St{}
ch <- x
// the following works
y := &St{}
ch <- y
mut z := &St{}
ch <- z
sem.wait()
println(z)
}
fn main() {
c := chan &St{}
sem := sync.new_semaphore()
go f(c, sem)
y := <-c
// this should fail
mut z := <-c
z.n = 9
sem.post()
println(y)
println(z)
}

View File

@ -58,8 +58,9 @@ pub fn (mut p Parser) parse_chan_type() table.Type {
}
p.register_auto_import('sync')
p.next()
is_mut := p.tok.kind == .key_mut
elem_type := p.parse_type()
idx := p.table.find_or_register_chan(elem_type)
idx := p.table.find_or_register_chan(elem_type, is_mut)
return table.new_type(idx)
}

View File

@ -762,6 +762,7 @@ pub mut:
pub struct Chan {
pub mut:
elem_type Type
is_mut bool
}
pub struct Map {

View File

@ -357,16 +357,26 @@ pub fn (t &Table) array_fixed_source_name(elem_type Type, size int) string {
}
[inline]
pub fn (t &Table) chan_name(elem_type Type) string {
pub fn (t &Table) chan_name(elem_type Type, is_mut bool) string {
elem_type_sym := t.get_type_symbol(elem_type)
suffix := if elem_type.is_ptr() { '_ptr' } else { '' }
mut suffix := ''
if is_mut {
suffix = '_mut'
} else if elem_type.is_ptr() {
suffix = '_ptr'
}
return 'chan_$elem_type_sym.name' + suffix
}
[inline]
pub fn (t &Table) chan_source_name(elem_type Type) string {
pub fn (t &Table) chan_source_name(elem_type Type, is_mut bool) string {
elem_type_sym := t.get_type_symbol(elem_type)
ptr := if elem_type.is_ptr() { '&' } else { '' }
mut ptr := ''
if is_mut {
ptr = 'mut '
} else if elem_type.is_ptr() {
ptr = '&'
}
return 'chan $ptr$elem_type_sym.source_name'
}
@ -389,9 +399,9 @@ pub fn (t &Table) map_source_name(key_type, value_type Type) string {
return 'map[${key_type_sym.source_name}]$ptr$value_type_sym.source_name'
}
pub fn (mut t Table) find_or_register_chan(elem_type Type) int {
name := t.chan_name(elem_type)
source_name := t.chan_source_name(elem_type)
pub fn (mut t Table) find_or_register_chan(elem_type Type, is_mut bool) int {
name := t.chan_name(elem_type, is_mut)
source_name := t.chan_source_name(elem_type, is_mut)
// existing
existing_idx := t.type_idxs[name]
if existing_idx > 0 {
@ -405,6 +415,7 @@ pub fn (mut t Table) find_or_register_chan(elem_type Type) int {
source_name: source_name
info: Chan{
elem_type: elem_type
is_mut: is_mut
}
}
return t.register_type_symbol(chan_typ)

View File

@ -18,10 +18,11 @@ fn test_semaphore() {
// wait for the 2 coroutines to finish using the semaphore
stopwatch := time.new_stopwatch({})
mut elapsed := stopwatch.elapsed()
if !sem.timed_wait(50 * time.millisecond) {
if !sem.timed_wait(200 * time.millisecond) {
// we should come here due to timeout
elapsed = stopwatch.elapsed()
}
println('elapsed: ${f64(elapsed)/time.second}s')
assert elapsed >= 48* time.millisecond
elapsed_ms := f64(elapsed)/time.millisecond
println('elapsed: ${elapsed_ms:.1f}ms')
assert elapsed_ms >= 195.0
}