diff --git a/cmd/tools/vtest-self.v b/cmd/tools/vtest-self.v index 4f65305d1d..2edcbc6844 100644 --- a/cmd/tools/vtest-self.v +++ b/cmd/tools/vtest-self.v @@ -132,6 +132,7 @@ const ( 'vlib/v/tests/semaphore_timed_test.v', 'vlib/v/tests/shared_array_test.v', 'vlib/v/tests/shared_autolock_test.v', + 'vlib/v/tests/shared_elem_test.v', 'vlib/v/tests/shared_lock_2_test.v', 'vlib/v/tests/shared_lock_3_test.v', 'vlib/v/tests/shared_fn_return_test.v', diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 2080ee9e2b..ca5890dfa0 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -652,9 +652,17 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { field.pos) } } - if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() - && !expr_type.is_number() { - c.error('ref', field.pos) + if info_field.typ.has_flag(.shared_f) { + if !expr_type.has_flag(.shared_f) && expr_type.is_ptr() { + c.error('`shared` field must be initialized with `shared` or value', + field.pos) + } + } else { + if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() + && !expr_type.is_number() { + c.error('reference field must be initialized with reference', + field.pos) + } } struct_init.fields[i].typ = expr_type struct_init.fields[i].expected_type = info_field.typ diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 74941dc86f..26bdef3282 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1489,6 +1489,9 @@ fn (mut g Gen) for_in(it ast.ForInStmt) { fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw table.Type, expected_type table.Type) { got_type := g.table.mktyp(got_type_raw) exp_sym := g.table.get_type_symbol(expected_type) + expected_is_ptr := expected_type.is_ptr() + got_is_ptr := got_type.is_ptr() + got_sym := g.table.get_type_symbol(got_type) if exp_sym.kind == .interface_ && got_type_raw.idx() != expected_type.idx() && !expected_type.has_flag(.optional) { got_styp := g.cc_type(got_type) @@ -1506,16 +1509,13 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw table.Type, expected_t return } // cast to sum type + exp_styp := g.typ(expected_type) + got_styp := g.typ(got_type) if expected_type != table.void_type { - expected_is_ptr := expected_type.is_ptr() expected_deref_type := if expected_is_ptr { expected_type.deref() } else { expected_type } - got_is_ptr := got_type.is_ptr() got_deref_type := if got_is_ptr { got_type.deref() } else { got_type } if g.table.sumtype_has_variant(expected_deref_type, got_deref_type) { - exp_styp := g.typ(expected_type) - got_styp := g.typ(got_type) // got_idx := got_type.idx() - got_sym := g.table.get_type_symbol(got_type) got_sidx := g.type_sidx(got_type) // TODO: do we need 1-3? if expected_is_ptr && got_is_ptr { @@ -1560,12 +1560,28 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw table.Type, expected_t } } // Generic dereferencing logic - expected_sym := g.table.get_type_symbol(expected_type) - got_is_ptr := got_type.is_ptr() - expected_is_ptr := expected_type.is_ptr() neither_void := table.voidptr_type !in [got_type, expected_type] + to_shared := expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f) + && !expected_type.has_flag(.optional) + // from_shared := got_type_raw.has_flag(.shared_f) && !expected_type.has_flag(.shared_f) + if to_shared { + shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*` + if got_type_raw.is_ptr() { + g.error('cannot convert reference to `shared`', expr.position()) + } + if exp_sym.kind == .array { + g.writeln('($shared_styp*)__dup_shared_array(&($shared_styp){.val = ') + } else if exp_sym.kind == .map { + g.writeln('($shared_styp*)__dup_shared_map(&($shared_styp){.val = ') + } else { + g.writeln('($shared_styp*)__dup${shared_styp}(&($shared_styp){.val = ') + } + g.expr(expr) + g.writeln('}, sizeof($shared_styp))') + return + } if got_is_ptr && !expected_is_ptr && neither_void - && expected_sym.kind !in [.interface_, .placeholder] { + && exp_sym.kind !in [.interface_, .placeholder] { got_deref_type := got_type.deref() deref_sym := g.table.get_type_symbol(got_deref_type) deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx] @@ -4860,7 +4876,7 @@ const ( fn (mut g Gen) struct_init(struct_init ast.StructInit) { styp := g.typ(struct_init.typ) - mut shared_styp := '' // only needed for shared &St{... + mut shared_styp := '' // only needed for shared x := St{... if styp in c.skip_struct_init { // needed for c++ compilers g.go_back_out(3) @@ -4873,7 +4889,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { if is_amp { g.out.go_back(1) // delete the `&` already generated in `prefix_expr() } - if g.is_shared && !g.inside_opt_data { + if g.is_shared && !g.inside_opt_data && !g.is_array_set { mut shared_typ := struct_init.typ.set_flag(.shared_f) shared_styp = g.typ(shared_typ) g.writeln('($shared_styp*)__dup${shared_styp}(&($shared_styp){.val = ($styp){') @@ -4927,8 +4943,8 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { } } if !cloned { - if field.expected_type.is_ptr() && !(field.typ.is_ptr() - || field.typ.is_pointer()) && !field.typ.is_number() { + if (field.expected_type.is_ptr() && !field.expected_type.has_flag(.shared_f)) + && !(field.typ.is_ptr() || field.typ.is_pointer()) && !field.typ.is_number() { g.write('/* autoref */&') } g.expr_with_cast(field.expr, field.typ, field.expected_type) @@ -4993,8 +5009,9 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { } } if !cloned { - if sfield.expected_type.is_ptr() && !(sfield.typ.is_ptr() - || sfield.typ.is_pointer()) && !sfield.typ.is_number() { + if (sfield.expected_type.is_ptr() && !sfield.expected_type.has_flag(.shared_f)) + && !(sfield.typ.is_ptr() || sfield.typ.is_pointer()) + && !sfield.typ.is_number() { g.write('/* autoref */&') } g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type) @@ -5045,7 +5062,7 @@ fn (mut g Gen) struct_init(struct_init ast.StructInit) { g.write('\n#ifndef __cplusplus\n0\n#endif\n') } g.write('}') - if g.is_shared && !g.inside_opt_data { + if g.is_shared && !g.inside_opt_data && !g.is_array_set { g.write('}, sizeof($shared_styp))') } else if is_amp { g.write(', sizeof($styp))') diff --git a/vlib/v/parser/containers.v b/vlib/v/parser/containers.v index bf53d19e66..cdf231c369 100644 --- a/vlib/v/parser/containers.v +++ b/vlib/v/parser/containers.v @@ -27,7 +27,7 @@ fn (mut p Parser) array_init() ast.ArrayInit { line_nr := p.tok.line_nr p.next() // []string - if p.tok.kind in [.name, .amp, .lsbr] && p.tok.line_nr == line_nr { + if p.tok.kind in [.name, .amp, .lsbr, .key_shared] && p.tok.line_nr == line_nr { elem_type_pos = p.tok.position() elem_type = p.parse_type() // this is set here because it's a known type, others could be the diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 8ea80bf744..6c1fe619e2 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -199,6 +199,9 @@ pub fn (mut p Parser) parse_type() table.Type { } is_shared := p.tok.kind == .key_shared is_atomic := p.tok.kind == .key_atomic + if is_shared { + p.register_auto_import('sync') + } mut nr_muls := 0 if p.tok.kind == .key_mut || is_shared || is_atomic { nr_muls++ diff --git a/vlib/v/tests/shared_elem_test.v b/vlib/v/tests/shared_elem_test.v new file mode 100644 index 0000000000..fc57bb4671 --- /dev/null +++ b/vlib/v/tests/shared_elem_test.v @@ -0,0 +1,95 @@ +struct Xyz { +mut: + n int +} + +struct Abc { + a shared Xyz + i int +} + +fn test_shared_struct_in_struct() { + shared y := Xyz{ n: 7 } + z := Abc{ + a: y + } + u := Xyz{ n: 17 } + x := Abc{ + a: u + } + v := Abc{ + a: Xyz{ n: 5 } + i: 3 + } + shared f := x.a + shared g := v.a + shared h := z.a + a := rlock f { f.n } + b := rlock g { g.n } + c := rlock h { h.n } + assert a == 17 + assert b == 5 + assert c == 7 + assert v.i == 3 +} + +struct Efg { + a shared []f64 + i int +} + +fn test_shared_array_in_struct() { + x := Efg{ + a: [1.25, 2.75, 7, 13.0625] + i: 12 + } + shared t := x.a + lock t { + t[2] = -1.125 + } + shared tt := x.a + v := rlock tt { tt[3] } + w := rlock tt { tt[2] } + assert v == 13.0625 + assert w == -1.125 + assert x.i == 12 +} + +struct Hjk { + m shared map[string]f64 + i int +} + +fn test_shared_map_in_struct() { + x := Hjk{ + m: {'st': -6.0625, 'xy': 12.125, 'rz': 2.25} + i: 23 + } + shared k := x.m + lock k { + k['yxc'] = -23.5 + } + shared p := x.m + a := rlock p { p['xy'] } + b := rlock p { p['yxc'] } + assert a == 12.125 + assert b == -23.5 + assert x.i == 23 +} + +fn test_array_of_shared() { + mut a := []shared Xyz{len: 3} + a[0] = Xyz{ n: 3 } + a[1] = Xyz{ n: 7 } + a[2] = Xyz{ n: 13 } + shared p := a[0] + shared q := a[2] + lock q { + q.n = -17 + } + shared r := a[2] + e := rlock p { p.n } + f := rlock r { r.n } + assert e == 3 + assert f == -17 +}