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/shift_test.v',
|
||||||
'vlib/v/tests/str_gen_test.v',
|
'vlib/v/tests/str_gen_test.v',
|
||||||
'vlib/v/tests/string_interpolation_multi_return_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/struct_allow_both_field_defaults_and_skip_flag_test.v',
|
||||||
'vlib/v/tests/string_interpolation_test.v',
|
'vlib/v/tests/string_interpolation_test.v',
|
||||||
'vlib/v/tests/struct_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 {
|
pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) table.Type {
|
||||||
for i, expr in node.exprs {
|
for i, expr in node.exprs {
|
||||||
ftyp := c.expr(expr)
|
ftyp := c.expr(expr)
|
||||||
|
if ftyp.has_flag(.shared_f) {
|
||||||
|
c.fail_if_not_rlocked(expr, 'interpolation object')
|
||||||
|
}
|
||||||
node.expr_types << ftyp
|
node.expr_types << ftyp
|
||||||
typ := c.table.unalias_num_type(ftyp)
|
typ := c.table.unalias_num_type(ftyp)
|
||||||
mut fmt := node.fmts[i]
|
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 }
|
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
|
// returns name and position of variable that needs write lock
|
||||||
// also sets `is_changed` to true (TODO update the name to reflect this?)
|
// 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) {
|
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 {
|
} else {
|
||||||
if left_type.has_flag(.shared_f) {
|
if left_type.has_flag(.shared_f) {
|
||||||
to_lock, pos := c.needs_rlock(call_expr.left)
|
c.fail_if_not_rlocked(call_expr.left, 'receiver')
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!left_type_sym.is_builtin() && method.mod != 'builtin') && method.language == .v
|
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${
|
c.error('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${
|
||||||
i + 1}`', arg.expr.position())
|
i + 1}`', arg.expr.position())
|
||||||
} else {
|
} else {
|
||||||
to_lock, pos := c.needs_rlock(arg.expr)
|
if got_arg_typ.has_flag(.shared_f) {
|
||||||
if to_lock != '' {
|
c.fail_if_not_rlocked(arg.expr, 'argument')
|
||||||
c.error('$to_lock is `shared` and must be `rlock`ed or `locked` to be passed as non-mut argument',
|
|
||||||
pos)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1699,6 +1669,9 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
if call_expr.args.len > 0 {
|
if call_expr.args.len > 0 {
|
||||||
c.error('.str() method calls should have no arguments', call_expr.pos)
|
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
|
return table.string_type
|
||||||
}
|
}
|
||||||
// call struct field fn 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 {
|
if fn_name in ['println', 'print', 'eprintln', 'eprint'] && call_expr.args.len > 0 {
|
||||||
c.expected_type = table.string_type
|
c.expected_type = table.string_type
|
||||||
call_expr.args[0].typ = c.expr(call_expr.args[0].expr)
|
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])`
|
// TODO: optimize `struct T{} fn (t &T) str() string {return 'abc'} mut a := []&T{} a << &T{} println(a[0])`
|
||||||
// It currently generates:
|
// 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${
|
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())
|
i + 1}`', call_arg.expr.position())
|
||||||
} else {
|
} else {
|
||||||
to_lock, pos := c.needs_rlock(call_arg.expr)
|
if typ.has_flag(.shared_f) {
|
||||||
if to_lock != '' {
|
c.fail_if_not_rlocked(call_arg.expr, 'argument')
|
||||||
c.error('$to_lock is `shared` and must be `rlock`ed or `lock`ed to be passed as non-mut argument',
|
|
||||||
pos)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ vlib/v/checker/tests/shared_bad_args.vv:43:8: error: r is `shared` and must be `
|
||||||
| ^
|
| ^
|
||||||
44 | println(u)
|
44 | println(u)
|
||||||
45 | }
|
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 | }
|
45 | }
|
||||||
46 | lock r {
|
46 | lock r {
|
||||||
47 | v := r.s_val(s)
|
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)
|
67 | a_mut(mut a)
|
||||||
| ^
|
| ^
|
||||||
68 | }
|
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)
|
m_mut(mut m)
|
||||||
a_mut(mut a)
|
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
|
return
|
||||||
}
|
}
|
||||||
if node.name == 'str' {
|
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
|
mut has_cast := false
|
||||||
if left_sym.kind == .map && node.name in ['clone', 'move'] {
|
if left_sym.kind == .map && node.name in ['clone', 'move'] {
|
||||||
|
@ -622,7 +626,8 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||||
} else {
|
} else {
|
||||||
g.write('${name}(')
|
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
|
// The receiver is a reference, but the caller provided a value
|
||||||
// Add `&` automatically.
|
// Add `&` automatically.
|
||||||
// TODO same logic in call_args()
|
// TODO same logic in call_args()
|
||||||
|
@ -651,7 +656,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||||
}
|
}
|
||||||
g.write(embed_name)
|
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')
|
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) {
|
fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype table.Type) {
|
||||||
|
is_shared := etype.has_flag(.shared_f)
|
||||||
mut typ := etype
|
mut typ := etype
|
||||||
|
if is_shared {
|
||||||
|
typ = typ.clear_flag(.shared_f).set_nr_muls(0)
|
||||||
|
}
|
||||||
mut sym := g.table.get_type_symbol(typ)
|
mut sym := g.table.get_type_symbol(typ)
|
||||||
// when type is alias, print the aliased value
|
// when type is alias, print the aliased value
|
||||||
if mut sym.info is table.Alias {
|
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}(')
|
g.write('${str_fn_name}(')
|
||||||
if str_method_expects_ptr && !is_ptr {
|
if str_method_expects_ptr && !is_ptr {
|
||||||
g.write('&')
|
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('*')
|
g.write('*')
|
||||||
}
|
}
|
||||||
if expr is ast.ArrayInit {
|
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)
|
g.expr(expr)
|
||||||
|
if is_shared {
|
||||||
|
g.write('->val')
|
||||||
|
}
|
||||||
g.write(')')
|
g.write(')')
|
||||||
if is_ptr && !is_var_mut {
|
if is_ptr && !is_var_mut {
|
||||||
g.write(')')
|
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