checker/cgen: support `print*()`, `.str()` and '$x' for `shared` (#8771)

pull/8790/head
Uwe Krüger 2021-02-16 12:40:13 +01:00 committed by GitHub
parent 6813866141
commit 0520b755f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 141 additions and 42 deletions

View File

@ -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',

View File

@ -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]

View File

@ -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')
}
}
}

View File

@ -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 | }

View File

@ -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')
}

View File

@ -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')
}
}

View File

@ -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(')')

View File

@ -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}"
}