checker/channels: check `mut`/`&` state of transmitted objects (#6315)
parent
601d098b48
commit
2cb711ee15
|
@ -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,16 +1720,18 @@ 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 {
|
||||
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) {
|
||||
if left_first is ast.Ident {
|
||||
assigned_var := left_first
|
||||
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)
|
||||
|
@ -1727,6 +1739,19 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
|
|||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
is_decl := assign_stmt.op == .decl_assign
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
}
|
|
@ -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()
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -762,6 +762,7 @@ pub mut:
|
|||
pub struct Chan {
|
||||
pub mut:
|
||||
elem_type Type
|
||||
is_mut bool
|
||||
}
|
||||
|
||||
pub struct Map {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue