checker: obey [ref_only] tag, allow embedding in other ref struct (#8707)
parent
aa548f45ea
commit
40066a5daa
|
@ -127,6 +127,7 @@ const (
|
||||||
'vlib/v/tests/module_test.v',
|
'vlib/v/tests/module_test.v',
|
||||||
'vlib/v/tests/operator_overloading_with_string_interpolation_test.v',
|
'vlib/v/tests/operator_overloading_with_string_interpolation_test.v',
|
||||||
'vlib/v/tests/orm_sub_struct_test.v',
|
'vlib/v/tests/orm_sub_struct_test.v',
|
||||||
|
'vlib/v/tests/ref_struct_test.v',
|
||||||
'vlib/v/tests/repl/repl_test.v',
|
'vlib/v/tests/repl/repl_test.v',
|
||||||
'vlib/v/tests/semaphore_test.v',
|
'vlib/v/tests/semaphore_test.v',
|
||||||
'vlib/v/tests/shared_arg_test.v',
|
'vlib/v/tests/shared_arg_test.v',
|
||||||
|
|
|
@ -529,7 +529,7 @@ pub fn channel_select(mut channels []&Channel, dir []Direction, mut objrefs []vo
|
||||||
assert channels.len == dir.len
|
assert channels.len == dir.len
|
||||||
assert dir.len == objrefs.len
|
assert dir.len == objrefs.len
|
||||||
mut subscr := []Subscription{len: channels.len}
|
mut subscr := []Subscription{len: channels.len}
|
||||||
mut sem := Semaphore{}
|
mut sem := unsafe { Semaphore{} }
|
||||||
sem.init(0)
|
sem.init(0)
|
||||||
for i, ch in channels {
|
for i, ch in channels {
|
||||||
subscr[i].sem = &sem
|
subscr[i].sem = &sem
|
||||||
|
|
|
@ -28,10 +28,12 @@ fn C.sem_timedwait(voidptr, voidptr) int
|
||||||
fn C.sem_destroy(voidptr) int
|
fn C.sem_destroy(voidptr) int
|
||||||
|
|
||||||
// [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
|
// [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
|
||||||
|
[ref_only]
|
||||||
pub struct Mutex {
|
pub struct Mutex {
|
||||||
mutex C.pthread_mutex_t
|
mutex C.pthread_mutex_t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ref_only]
|
||||||
pub struct RwMutex {
|
pub struct RwMutex {
|
||||||
mutex C.pthread_rwlock_t
|
mutex C.pthread_rwlock_t
|
||||||
}
|
}
|
||||||
|
@ -40,6 +42,7 @@ struct RwMutexAttr {
|
||||||
attr C.pthread_rwlockattr_t
|
attr C.pthread_rwlockattr_t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ref_only]
|
||||||
struct Semaphore {
|
struct Semaphore {
|
||||||
sem C.sem_t
|
sem C.sem_t
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,12 @@ fn C.pthread_cond_timedwait(voidptr, voidptr, voidptr) int
|
||||||
fn C.pthread_cond_destroy(voidptr) int
|
fn C.pthread_cond_destroy(voidptr) int
|
||||||
|
|
||||||
// [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
|
// [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
|
||||||
|
[ref_only]
|
||||||
pub struct Mutex {
|
pub struct Mutex {
|
||||||
mutex C.pthread_mutex_t
|
mutex C.pthread_mutex_t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ref_only]
|
||||||
pub struct RwMutex {
|
pub struct RwMutex {
|
||||||
mutex C.pthread_rwlock_t
|
mutex C.pthread_rwlock_t
|
||||||
}
|
}
|
||||||
|
@ -49,6 +51,7 @@ struct CondAttr {
|
||||||
|
|
||||||
/* MacOSX has no unnamed semaphores and no `timed_wait()` at all
|
/* MacOSX has no unnamed semaphores and no `timed_wait()` at all
|
||||||
so we emulate the behaviour with other devices */
|
so we emulate the behaviour with other devices */
|
||||||
|
[ref_only]
|
||||||
struct Semaphore {
|
struct Semaphore {
|
||||||
mtx C.pthread_mutex_t
|
mtx C.pthread_mutex_t
|
||||||
cond C.pthread_cond_t
|
cond C.pthread_cond_t
|
||||||
|
|
|
@ -22,16 +22,19 @@ type SHANDLE = voidptr
|
||||||
//[init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
|
//[init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function.
|
||||||
|
|
||||||
// `SRWLOCK` is much more performant that `Mutex` on Windows, so use that in both cases since we don't want to share with other processes
|
// `SRWLOCK` is much more performant that `Mutex` on Windows, so use that in both cases since we don't want to share with other processes
|
||||||
|
[ref_only]
|
||||||
pub struct Mutex {
|
pub struct Mutex {
|
||||||
mut:
|
mut:
|
||||||
mx C.SRWLOCK // mutex handle
|
mx C.SRWLOCK // mutex handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ref_only]
|
||||||
pub struct RwMutex {
|
pub struct RwMutex {
|
||||||
mut:
|
mut:
|
||||||
mx C.SRWLOCK // mutex handle
|
mx C.SRWLOCK // mutex handle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ref_only]
|
||||||
struct Semaphore {
|
struct Semaphore {
|
||||||
mtx C.SRWLOCK
|
mtx C.SRWLOCK
|
||||||
cond C.CONDITION_VARIABLE
|
cond C.CONDITION_VARIABLE
|
||||||
|
|
|
@ -34,7 +34,7 @@ a new preference is created:
|
||||||
```v
|
```v
|
||||||
import v.pref
|
import v.pref
|
||||||
|
|
||||||
pref := pref.Preferences{}
|
pref := &pref.Preferences{}
|
||||||
```
|
```
|
||||||
|
|
||||||
and a new scope is created:
|
and a new scope is created:
|
||||||
|
|
|
@ -60,6 +60,7 @@ pub mut:
|
||||||
inside_unsafe bool
|
inside_unsafe bool
|
||||||
inside_const bool
|
inside_const bool
|
||||||
inside_anon_fn bool
|
inside_anon_fn bool
|
||||||
|
inside_ref_lit bool
|
||||||
skip_flags bool // should `#flag` and `#include` be skipped
|
skip_flags bool // should `#flag` and `#include` be skipped
|
||||||
cur_generic_types []table.Type
|
cur_generic_types []table.Type
|
||||||
mut:
|
mut:
|
||||||
|
@ -372,13 +373,6 @@ pub fn (mut c Checker) interface_decl(decl ast.InterfaceDecl) {
|
||||||
field.type_pos)
|
field.type_pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sym.kind == .struct_ {
|
|
||||||
info := sym.info as table.Struct
|
|
||||||
if info.is_ref_only && !field.typ.is_ptr() {
|
|
||||||
c.error('`$sym.name` type can only be used as a reference: `&$sym.name`',
|
|
||||||
field.type_pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if sym.kind == .map {
|
if sym.kind == .map {
|
||||||
info := sym.map_info()
|
info := sym.map_info()
|
||||||
key_sym := c.table.get_type_symbol(info.key_type)
|
key_sym := c.table.get_type_symbol(info.key_type)
|
||||||
|
@ -403,6 +397,11 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
|
||||||
embed_sym := c.table.get_type_symbol(embed.typ)
|
embed_sym := c.table.get_type_symbol(embed.typ)
|
||||||
if embed_sym.kind != .struct_ {
|
if embed_sym.kind != .struct_ {
|
||||||
c.error('`$embed_sym.name` is not a struct', embed.pos)
|
c.error('`$embed_sym.name` is not a struct', embed.pos)
|
||||||
|
} else {
|
||||||
|
info := embed_sym.info as table.Struct
|
||||||
|
if info.is_ref_only && !embed.typ.is_ptr() {
|
||||||
|
struct_sym.info.is_ref_only = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for attr in decl.attrs {
|
for attr in decl.attrs {
|
||||||
|
@ -445,8 +444,7 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
|
||||||
if sym.kind == .struct_ {
|
if sym.kind == .struct_ {
|
||||||
info := sym.info as table.Struct
|
info := sym.info as table.Struct
|
||||||
if info.is_ref_only && !field.typ.is_ptr() {
|
if info.is_ref_only && !field.typ.is_ptr() {
|
||||||
c.error('`$sym.name` type can only be used as a reference: `&$sym.name`',
|
struct_sym.info.is_ref_only = true
|
||||||
field.type_pos)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sym.kind == .map {
|
if sym.kind == .map {
|
||||||
|
@ -545,6 +543,10 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
||||||
c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' +
|
c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' +
|
||||||
'it cannot be initialized with `$type_sym.name{}`', struct_init.pos)
|
'it cannot be initialized with `$type_sym.name{}`', struct_init.pos)
|
||||||
}
|
}
|
||||||
|
if info.is_ref_only && !c.inside_ref_lit && !c.inside_unsafe && !struct_init.typ.is_ptr() {
|
||||||
|
c.error('`$type_sym.name` type can only be used as a reference `&$type_sym.name` or inside a `struct` reference',
|
||||||
|
struct_init.pos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if type_sym.name.len == 1 && c.cur_fn.generic_params.len == 0 {
|
if type_sym.name.len == 1 && c.cur_fn.generic_params.len == 0 {
|
||||||
c.error('unknown struct `$type_sym.name`', struct_init.pos)
|
c.error('unknown struct `$type_sym.name`', struct_init.pos)
|
||||||
|
@ -2610,7 +2612,14 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if assign_stmt.right_types.len < assign_stmt.left.len { // first type or multi return types added above
|
if assign_stmt.right_types.len < assign_stmt.left.len { // first type or multi return types added above
|
||||||
|
old_inside_ref_lit := c.inside_ref_lit
|
||||||
|
if left is ast.Ident {
|
||||||
|
if left.info is ast.IdentVar {
|
||||||
|
c.inside_ref_lit = c.inside_ref_lit || left.info.share == .shared_t
|
||||||
|
}
|
||||||
|
}
|
||||||
right_type := c.expr(assign_stmt.right[i])
|
right_type := c.expr(assign_stmt.right[i])
|
||||||
|
c.inside_ref_lit = old_inside_ref_lit
|
||||||
if assign_stmt.right_types.len == i {
|
if assign_stmt.right_types.len == i {
|
||||||
assign_stmt.right_types << c.check_expr_opt_call(assign_stmt.right[i],
|
assign_stmt.right_types << c.check_expr_opt_call(assign_stmt.right[i],
|
||||||
right_type)
|
right_type)
|
||||||
|
@ -2875,7 +2884,14 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
|
||||||
left_first := assign_stmt.left[0]
|
left_first := assign_stmt.left[0]
|
||||||
if left_first is ast.Ident {
|
if left_first is ast.Ident {
|
||||||
assigned_var := left_first
|
assigned_var := left_first
|
||||||
|
mut is_shared := false
|
||||||
|
if left_first.info is ast.IdentVar {
|
||||||
|
is_shared = left_first.info.share == .shared_t
|
||||||
|
}
|
||||||
|
old_inside_ref_lit := c.inside_ref_lit
|
||||||
|
c.inside_ref_lit = (c.inside_ref_lit || node.op == .amp || is_shared)
|
||||||
c.expr(node.right)
|
c.expr(node.right)
|
||||||
|
c.inside_ref_lit = old_inside_ref_lit
|
||||||
if node.right is ast.Ident {
|
if node.right is ast.Ident {
|
||||||
if node.right.obj is ast.Var {
|
if node.right.obj is ast.Var {
|
||||||
v := node.right.obj
|
v := node.right.obj
|
||||||
|
@ -3921,7 +3937,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) table.Type {
|
||||||
if node.is_vweb {
|
if node.is_vweb {
|
||||||
// TODO assoc parser bug
|
// TODO assoc parser bug
|
||||||
pref_ := *c.pref
|
pref_ := *c.pref
|
||||||
pref2 := pref.Preferences{
|
pref2 := &pref.Preferences{
|
||||||
...pref_
|
...pref_
|
||||||
is_vweb: true
|
is_vweb: true
|
||||||
}
|
}
|
||||||
|
@ -5121,7 +5137,10 @@ pub fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) table.Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) table.Type {
|
pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) table.Type {
|
||||||
|
old_inside_ref_lit := c.inside_ref_lit
|
||||||
|
c.inside_ref_lit = c.inside_ref_lit || node.op == .amp
|
||||||
right_type := c.expr(node.right)
|
right_type := c.expr(node.right)
|
||||||
|
c.inside_ref_lit = old_inside_ref_lit
|
||||||
node.right_type = right_type
|
node.right_type = right_type
|
||||||
// TODO: testing ref/deref strategy
|
// TODO: testing ref/deref strategy
|
||||||
if node.op == .amp && !right_type.is_ptr() {
|
if node.op == .amp && !right_type.is_ptr() {
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
vlib/v/checker/tests/ref_only_struct.vv:18:7: error: `Abc` type can only be used as a reference `&Abc` or inside a `struct` reference
|
||||||
|
16 |
|
||||||
|
17 | fn main() {
|
||||||
|
18 | a := Abc{ n: 3 }
|
||||||
|
| ~~~~~~~~~~~
|
||||||
|
19 | b := St{
|
||||||
|
20 | Abc{ n: 7 }
|
||||||
|
vlib/v/checker/tests/ref_only_struct.vv:19:7: error: `St` type can only be used as a reference `&St` or inside a `struct` reference
|
||||||
|
17 | fn main() {
|
||||||
|
18 | a := Abc{ n: 3 }
|
||||||
|
19 | b := St{
|
||||||
|
| ~~~
|
||||||
|
20 | Abc{ n: 7 }
|
||||||
|
21 | }
|
||||||
|
vlib/v/checker/tests/ref_only_struct.vv:20:3: error: `Abc` type can only be used as a reference `&Abc` or inside a `struct` reference
|
||||||
|
18 | a := Abc{ n: 3 }
|
||||||
|
19 | b := St{
|
||||||
|
20 | Abc{ n: 7 }
|
||||||
|
| ~~~~~~~~~~~
|
||||||
|
21 | }
|
||||||
|
22 | x := Qwe{
|
||||||
|
vlib/v/checker/tests/ref_only_struct.vv:22:7: error: `Qwe` type can only be used as a reference `&Qwe` or inside a `struct` reference
|
||||||
|
20 | Abc{ n: 7 }
|
||||||
|
21 | }
|
||||||
|
22 | x := Qwe{
|
||||||
|
| ~~~~
|
||||||
|
23 | f: 12.25
|
||||||
|
24 | a: Abc{ n: 23 }
|
||||||
|
vlib/v/checker/tests/ref_only_struct.vv:24:6: error: `Abc` type can only be used as a reference `&Abc` or inside a `struct` reference
|
||||||
|
22 | x := Qwe{
|
||||||
|
23 | f: 12.25
|
||||||
|
24 | a: Abc{ n: 23 }
|
||||||
|
| ~~~~~~~~~~~~
|
||||||
|
25 | }
|
||||||
|
26 | println('$a.n $b.n $x.a.n')
|
|
@ -0,0 +1,27 @@
|
||||||
|
[ref_only]
|
||||||
|
struct Abc {
|
||||||
|
mut:
|
||||||
|
n int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct St {
|
||||||
|
Abc
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Qwe {
|
||||||
|
mut:
|
||||||
|
f f64
|
||||||
|
a Abc
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
a := Abc{ n: 3 }
|
||||||
|
b := St{
|
||||||
|
Abc{ n: 7 }
|
||||||
|
}
|
||||||
|
x := Qwe{
|
||||||
|
f: 12.25
|
||||||
|
a: Abc{ n: 23 }
|
||||||
|
}
|
||||||
|
println('$a.n $b.n $x.a.n')
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ fn test_c_files() {
|
||||||
for i in 1 .. (nr_tests + 1) {
|
for i in 1 .. (nr_tests + 1) {
|
||||||
path := '$vroot/vlib/v/gen/tests/${i}.vv'
|
path := '$vroot/vlib/v/gen/tests/${i}.vv'
|
||||||
ctext := os.read_file('$vroot/vlib/v/gen/tests/${i}.c') or { panic(err) }
|
ctext := os.read_file('$vroot/vlib/v/gen/tests/${i}.c') or { panic(err) }
|
||||||
mut b := builder.new_builder(pref.Preferences{})
|
mut b := builder.new_builder(&pref.Preferences{})
|
||||||
b.module_search_paths = ['$vroot/vlib/v/gen/tests/']
|
b.module_search_paths = ['$vroot/vlib/v/gen/tests/']
|
||||||
mut res := b.gen_c([path]).after('#endbuiltin')
|
mut res := b.gen_c([path]).after('#endbuiltin')
|
||||||
if res.contains('string _STR') {
|
if res.contains('string _STR') {
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
[ref_only]
|
||||||
|
struct Abc {
|
||||||
|
mut:
|
||||||
|
n int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct St {
|
||||||
|
Abc
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Qwe {
|
||||||
|
mut:
|
||||||
|
f f64
|
||||||
|
a Abc
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Rtz {
|
||||||
|
mut:
|
||||||
|
f f64
|
||||||
|
a &Abc
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_ref() {
|
||||||
|
a := &Abc{ n: 3 }
|
||||||
|
assert a.n == 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_shared() {
|
||||||
|
shared a := Abc{ n: 4 }
|
||||||
|
res := rlock a { a.n }
|
||||||
|
assert res == 4
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_embed_in_ref_struct() {
|
||||||
|
a := &St{
|
||||||
|
Abc{ n: 5 }
|
||||||
|
}
|
||||||
|
assert a.n == 5
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_field_in_ref_struct() {
|
||||||
|
x := &Qwe{
|
||||||
|
f: 12.25
|
||||||
|
a: Abc{ n: 23 }
|
||||||
|
}
|
||||||
|
assert x.a.n == 23
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_ref_field() {
|
||||||
|
y := Rtz{
|
||||||
|
f: -6.25
|
||||||
|
a: &Abc{ n: 29 }
|
||||||
|
}
|
||||||
|
assert y.a.n == 29
|
||||||
|
}
|
|
@ -35,8 +35,8 @@ pub struct SSEMessage {
|
||||||
retry int
|
retry int
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_connection(conn &net.TcpConn) SSEConnection {
|
pub fn new_connection(conn &net.TcpConn) &SSEConnection {
|
||||||
return SSEConnection{
|
return &SSEConnection{
|
||||||
conn: conn
|
conn: conn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue