checker/cgen: support `print*()`, `.str()` and '$x' for `shared` (#8771)
parent
6813866141
commit
0520b755f4
|
@ -148,6 +148,7 @@ const (
|
|||
'vlib/v/tests/shift_test.v',
|
||||
'vlib/v/tests/str_gen_test.v',
|
||||
'vlib/v/tests/string_interpolation_multi_return_test.v',
|
||||
'vlib/v/tests/string_interpolation_shared_test.v',
|
||||
'vlib/v/tests/struct_allow_both_field_defaults_and_skip_flag_test.v',
|
||||
'vlib/v/tests/string_interpolation_test.v',
|
||||
'vlib/v/tests/struct_test.v',
|
||||
|
|
|
@ -307,9 +307,25 @@ pub fn (c &Checker) get_default_fmt(ftyp table.Type, typ table.Type) byte {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) fail_if_not_rlocked(expr ast.Expr, what string) {
|
||||
if expr is ast.Ident {
|
||||
if expr.name !in c.rlocked_names && expr.name !in c.locked_names {
|
||||
action := if what == 'argument' { 'passed' } else { 'used' }
|
||||
c.error('$expr.name is `shared` and must be `rlock`ed or `lock`ed to be $action as non-mut $what',
|
||||
expr.pos)
|
||||
}
|
||||
} else {
|
||||
c.error('you have to create a handle and `rlock` it to use a `shared` element as non-mut $what',
|
||||
expr.position())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) table.Type {
|
||||
for i, expr in node.exprs {
|
||||
ftyp := c.expr(expr)
|
||||
if ftyp.has_flag(.shared_f) {
|
||||
c.fail_if_not_rlocked(expr, 'interpolation object')
|
||||
}
|
||||
node.expr_types << ftyp
|
||||
typ := c.table.unalias_num_type(ftyp)
|
||||
mut fmt := node.fmts[i]
|
||||
|
|
|
@ -1055,30 +1055,6 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
|
|||
return if infix_expr.op.is_relational() { table.bool_type } else { return_type }
|
||||
}
|
||||
|
||||
// returns name and position of variable that needs write lock
|
||||
fn (mut c Checker) needs_rlock(expr ast.Expr) (string, token.Position) {
|
||||
mut to_lock := '' // name of variable that needs lock
|
||||
mut pos := token.Position{} // and its position
|
||||
match mut expr {
|
||||
ast.Ident {
|
||||
if expr.obj is ast.Var {
|
||||
mut v := expr.obj as ast.Var
|
||||
if v.typ.share() == .shared_t {
|
||||
if expr.name !in c.rlocked_names && expr.name !in c.locked_names {
|
||||
to_lock = expr.name
|
||||
pos = expr.pos
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
to_lock = ''
|
||||
pos = token.Position{}
|
||||
}
|
||||
}
|
||||
return to_lock, pos
|
||||
}
|
||||
|
||||
// returns name and position of variable that needs write lock
|
||||
// also sets `is_changed` to true (TODO update the name to reflect this?)
|
||||
fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
|
||||
|
@ -1540,11 +1516,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
}
|
||||
} else {
|
||||
if left_type.has_flag(.shared_f) {
|
||||
to_lock, pos := c.needs_rlock(call_expr.left)
|
||||
if to_lock != '' {
|
||||
c.error('$to_lock is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut receiver',
|
||||
pos)
|
||||
}
|
||||
c.fail_if_not_rlocked(call_expr.left, 'receiver')
|
||||
}
|
||||
}
|
||||
if (!left_type_sym.is_builtin() && method.mod != 'builtin') && method.language == .v
|
||||
|
@ -1639,10 +1611,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
c.error('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${
|
||||
i + 1}`', arg.expr.position())
|
||||
} else {
|
||||
to_lock, pos := c.needs_rlock(arg.expr)
|
||||
if to_lock != '' {
|
||||
c.error('$to_lock is `shared` and must be `rlock`ed or `locked` to be passed as non-mut argument',
|
||||
pos)
|
||||
if got_arg_typ.has_flag(.shared_f) {
|
||||
c.fail_if_not_rlocked(arg.expr, 'argument')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1699,6 +1669,9 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
if call_expr.args.len > 0 {
|
||||
c.error('.str() method calls should have no arguments', call_expr.pos)
|
||||
}
|
||||
if left_type.has_flag(.shared_f) {
|
||||
c.fail_if_not_rlocked(call_expr.left, 'receiver')
|
||||
}
|
||||
return table.string_type
|
||||
}
|
||||
// call struct field fn type
|
||||
|
@ -1937,6 +1910,9 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
if fn_name in ['println', 'print', 'eprintln', 'eprint'] && call_expr.args.len > 0 {
|
||||
c.expected_type = table.string_type
|
||||
call_expr.args[0].typ = c.expr(call_expr.args[0].expr)
|
||||
if call_expr.args[0].typ.has_flag(.shared_f) {
|
||||
c.fail_if_not_rlocked(call_expr.args[0].expr, 'argument to print')
|
||||
}
|
||||
/*
|
||||
// TODO: optimize `struct T{} fn (t &T) str() string {return 'abc'} mut a := []&T{} a << &T{} println(a[0])`
|
||||
// It currently generates:
|
||||
|
@ -1999,10 +1975,8 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||
c.error('`$call_expr.name` parameter `$arg.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${
|
||||
i + 1}`', call_arg.expr.position())
|
||||
} else {
|
||||
to_lock, pos := c.needs_rlock(call_arg.expr)
|
||||
if to_lock != '' {
|
||||
c.error('$to_lock is `shared` and must be `rlock`ed or `lock`ed to be passed as non-mut argument',
|
||||
pos)
|
||||
if typ.has_flag(.shared_f) {
|
||||
c.fail_if_not_rlocked(call_arg.expr, 'argument')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ vlib/v/checker/tests/shared_bad_args.vv:43:8: error: r is `shared` and must be `
|
|||
| ^
|
||||
44 | println(u)
|
||||
45 | }
|
||||
vlib/v/checker/tests/shared_bad_args.vv:47:16: error: s is `shared` and must be `rlock`ed or `locked` to be passed as non-mut argument
|
||||
vlib/v/checker/tests/shared_bad_args.vv:47:16: error: s is `shared` and must be `rlock`ed or `lock`ed to be passed as non-mut argument
|
||||
45 | }
|
||||
46 | lock r {
|
||||
47 | v := r.s_val(s)
|
||||
|
@ -53,3 +53,31 @@ vlib/v/checker/tests/shared_bad_args.vv:67:12: error: a is `shared` and must be
|
|||
67 | a_mut(mut a)
|
||||
| ^
|
||||
68 | }
|
||||
69 |
|
||||
vlib/v/checker/tests/shared_bad_args.vv:76:10: error: y is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut argument to print
|
||||
74 | fn main() {
|
||||
75 | shared y := St{ a: 5 }
|
||||
76 | println(y)
|
||||
| ^
|
||||
77 | println('$y')
|
||||
78 | a := Ab{ s: St{ a: 3 } }
|
||||
vlib/v/checker/tests/shared_bad_args.vv:77:12: error: y is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut interpolation object
|
||||
75 | shared y := St{ a: 5 }
|
||||
76 | println(y)
|
||||
77 | println('$y')
|
||||
| ^
|
||||
78 | a := Ab{ s: St{ a: 3 } }
|
||||
79 | println(a.s)
|
||||
vlib/v/checker/tests/shared_bad_args.vv:79:12: error: you have to create a handle and `rlock` it to use a `shared` element as non-mut argument to print
|
||||
77 | println('$y')
|
||||
78 | a := Ab{ s: St{ a: 3 } }
|
||||
79 | println(a.s)
|
||||
| ^
|
||||
80 | println('$a.s')
|
||||
81 | }
|
||||
vlib/v/checker/tests/shared_bad_args.vv:80:14: error: you have to create a handle and `rlock` it to use a `shared` element as non-mut interpolation object
|
||||
78 | a := Ab{ s: St{ a: 3 } }
|
||||
79 | println(a.s)
|
||||
80 | println('$a.s')
|
||||
| ^
|
||||
81 | }
|
||||
|
|
|
@ -66,3 +66,16 @@ fn test_shared_as_mut() {
|
|||
m_mut(mut m)
|
||||
a_mut(mut a)
|
||||
}
|
||||
|
||||
struct Ab {
|
||||
s shared St
|
||||
}
|
||||
|
||||
fn main() {
|
||||
shared y := St{ a: 5 }
|
||||
println(y)
|
||||
println('$y')
|
||||
a := Ab{ s: St{ a: 3 } }
|
||||
println(a.s)
|
||||
println('$a.s')
|
||||
}
|
||||
|
|
|
@ -546,7 +546,11 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
return
|
||||
}
|
||||
if node.name == 'str' {
|
||||
g.gen_str_for_type(node.receiver_type)
|
||||
mut rec_type := node.receiver_type
|
||||
if rec_type.has_flag(.shared_f) {
|
||||
rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0)
|
||||
}
|
||||
g.gen_str_for_type(rec_type)
|
||||
}
|
||||
mut has_cast := false
|
||||
if left_sym.kind == .map && node.name in ['clone', 'move'] {
|
||||
|
@ -622,7 +626,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
} else {
|
||||
g.write('${name}(')
|
||||
}
|
||||
if node.receiver_type.is_ptr() && (!node.left_type.is_ptr() || node.from_embed_type != 0) {
|
||||
if node.receiver_type.is_ptr() && (!node.left_type.is_ptr()
|
||||
|| node.from_embed_type != 0 || (node.left_type.has_flag(.shared_f) && node.name != 'str')) {
|
||||
// The receiver is a reference, but the caller provided a value
|
||||
// Add `&` automatically.
|
||||
// TODO same logic in call_args()
|
||||
|
@ -651,7 +656,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
}
|
||||
g.write(embed_name)
|
||||
}
|
||||
if node.left_type.has_flag(.shared_f) && !node.receiver_type.is_ptr() {
|
||||
if node.left_type.has_flag(.shared_f) {
|
||||
g.write('->val')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -302,7 +302,11 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
|
|||
}
|
||||
|
||||
fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) {
|
||||
is_shared := etype.has_flag(.shared_f)
|
||||
mut typ := etype
|
||||
if is_shared {
|
||||
typ = typ.clear_flag(.shared_f).set_nr_muls(0)
|
||||
}
|
||||
mut sym := g.table.get_type_symbol(typ)
|
||||
// when type is alias, print the aliased value
|
||||
if mut sym.info is table.Alias {
|
||||
|
@ -351,7 +355,7 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) {
|
|||
g.write('${str_fn_name}(')
|
||||
if str_method_expects_ptr && !is_ptr {
|
||||
g.write('&')
|
||||
} else if (!str_method_expects_ptr && is_ptr) || is_var_mut {
|
||||
} else if (!str_method_expects_ptr && is_ptr && !is_shared) || is_var_mut {
|
||||
g.write('*')
|
||||
}
|
||||
if expr is ast.ArrayInit {
|
||||
|
@ -361,6 +365,9 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) {
|
|||
}
|
||||
}
|
||||
g.expr(expr)
|
||||
if is_shared {
|
||||
g.write('->val')
|
||||
}
|
||||
g.write(')')
|
||||
if is_ptr && !is_var_mut {
|
||||
g.write(')')
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
struct Abc {
|
||||
mut:
|
||||
x f64
|
||||
}
|
||||
|
||||
struct Qwe {
|
||||
s shared Abc
|
||||
}
|
||||
|
||||
fn test_shared_struct_interpolation() {
|
||||
shared s := Abc{ x: 6.25 }
|
||||
astr := rlock s {
|
||||
'$s'
|
||||
}
|
||||
assert astr.starts_with('Abc{')
|
||||
assert astr.contains('x: 6.25')
|
||||
assert astr.ends_with('}')
|
||||
}
|
||||
|
||||
fn test_shared_array_interpolation() {
|
||||
shared a := [0.25, -6.125, 12.5]
|
||||
astr := rlock a {
|
||||
'$a'
|
||||
}
|
||||
assert astr == '[0.25, -6.125, 12.5]'
|
||||
}
|
||||
|
||||
fn test_shared_map_interpolation() {
|
||||
shared m := {'xy': 12.125, 'qwe': -6.0625, 'foo': 0.5}
|
||||
mstr := rlock m {
|
||||
'$m'
|
||||
}
|
||||
assert mstr == "{'xy': 12.125, 'qwe': -6.0625, 'foo': 0.5}"
|
||||
}
|
||||
|
||||
fn test_print_shared() {
|
||||
shared s := Abc{ x: 6.25 }
|
||||
shared a := [0.25, -6.125, 12.5]
|
||||
shared m := {'xy': 12.125, 'qwe': -6.0625, 'foo': 0.5}
|
||||
x, y := rlock a, s {
|
||||
println(s)
|
||||
println(a)
|
||||
s.str(), a.str()
|
||||
}
|
||||
z := rlock m {
|
||||
println(m)
|
||||
m.str()
|
||||
}
|
||||
assert x.starts_with('Abc{')
|
||||
assert x.contains('x: 6.25')
|
||||
assert x.ends_with('}')
|
||||
assert y == '[0.25, -6.125, 12.5]'
|
||||
assert z == "{'xy': 12.125, 'qwe': -6.0625, 'foo': 0.5}"
|
||||
}
|
||||
|
Loading…
Reference in New Issue