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`
|
.arrow { // `chan <- elem`
|
||||||
if left.kind == .chan {
|
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) {
|
if !c.check_types(right_type, elem_type) {
|
||||||
c.error('cannot push `$right.name` on `$left.name`', right_pos)
|
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 {
|
} else {
|
||||||
c.error('cannot push on non-channel `$left.name`', left_pos)
|
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]
|
right_first := assign_stmt.right[0]
|
||||||
mut right_len := assign_stmt.right.len
|
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 {
|
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 = [
|
assign_stmt.right_types = [
|
||||||
c.check_expr_opt_call(right_first, right_type0),
|
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
|
return
|
||||||
}
|
}
|
||||||
// Check `x := &y`
|
// Check `x := &y` and `mut x := <-ch`
|
||||||
if right_first is ast.PrefixExpr {
|
if right_first is ast.PrefixExpr {
|
||||||
node := right_first
|
node := right_first
|
||||||
left_first := assign_stmt.left[0]
|
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
|
ident := node.right as ast.Ident
|
||||||
scope := c.file.scope.innermost(node.pos.pos)
|
scope := c.file.scope.innermost(node.pos.pos)
|
||||||
if v := scope.find_var(ident.name) {
|
if v := scope.find_var(ident.name) {
|
||||||
if left_first is ast.Ident {
|
right_type0 = v.typ
|
||||||
assigned_var := left_first
|
if node.op == .amp {
|
||||||
if !v.is_mut && assigned_var.is_mut && !c.inside_unsafe {
|
if !v.is_mut && assigned_var.is_mut && !c.inside_unsafe {
|
||||||
c.error('`$ident.name` is immutable, cannot have a mutable reference to it',
|
c.error('`$ident.name` is immutable, cannot have a mutable reference to it',
|
||||||
node.pos)
|
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
|
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.register_auto_import('sync')
|
||||||
p.next()
|
p.next()
|
||||||
|
is_mut := p.tok.kind == .key_mut
|
||||||
elem_type := p.parse_type()
|
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)
|
return table.new_type(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -762,6 +762,7 @@ pub mut:
|
||||||
pub struct Chan {
|
pub struct Chan {
|
||||||
pub mut:
|
pub mut:
|
||||||
elem_type Type
|
elem_type Type
|
||||||
|
is_mut bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Map {
|
pub struct Map {
|
||||||
|
|
|
@ -357,16 +357,26 @@ pub fn (t &Table) array_fixed_source_name(elem_type Type, size int) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
[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)
|
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
|
return 'chan_$elem_type_sym.name' + suffix
|
||||||
}
|
}
|
||||||
|
|
||||||
[inline]
|
[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)
|
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'
|
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'
|
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 {
|
pub fn (mut t Table) find_or_register_chan(elem_type Type, is_mut bool) int {
|
||||||
name := t.chan_name(elem_type)
|
name := t.chan_name(elem_type, is_mut)
|
||||||
source_name := t.chan_source_name(elem_type)
|
source_name := t.chan_source_name(elem_type, is_mut)
|
||||||
// existing
|
// existing
|
||||||
existing_idx := t.type_idxs[name]
|
existing_idx := t.type_idxs[name]
|
||||||
if existing_idx > 0 {
|
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
|
source_name: source_name
|
||||||
info: Chan{
|
info: Chan{
|
||||||
elem_type: elem_type
|
elem_type: elem_type
|
||||||
|
is_mut: is_mut
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return t.register_type_symbol(chan_typ)
|
return t.register_type_symbol(chan_typ)
|
||||||
|
|
|
@ -18,10 +18,11 @@ fn test_semaphore() {
|
||||||
// wait for the 2 coroutines to finish using the semaphore
|
// wait for the 2 coroutines to finish using the semaphore
|
||||||
stopwatch := time.new_stopwatch({})
|
stopwatch := time.new_stopwatch({})
|
||||||
mut elapsed := stopwatch.elapsed()
|
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
|
// we should come here due to timeout
|
||||||
elapsed = stopwatch.elapsed()
|
elapsed = stopwatch.elapsed()
|
||||||
}
|
}
|
||||||
println('elapsed: ${f64(elapsed)/time.second}s')
|
elapsed_ms := f64(elapsed)/time.millisecond
|
||||||
assert elapsed >= 48* time.millisecond
|
println('elapsed: ${elapsed_ms:.1f}ms')
|
||||||
|
assert elapsed_ms >= 195.0
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue