diff --git a/vlib/os/file.v b/vlib/os/file.v index ed3a08c94d..45b824c95e 100644 --- a/vlib/os/file.v +++ b/vlib/os/file.v @@ -86,7 +86,7 @@ pub fn (f &File) read_bytes(size int) []byte { // read_bytes_at reads bytes at the given position in the file pub fn (f &File) read_bytes_at(size, pos int) []byte { mut arr := []byte{len: size} - nreadbytes := f.read_bytes_into(pos, arr) or { + nreadbytes := f.read_bytes_into(pos, mut arr) or { // return err return [] } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 82ea0ec0af..5e6748a8f6 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -286,6 +286,7 @@ pub mut: return_type table.Type should_be_skipped bool generic_type table.Type // TODO array, to support multiple types + autofree_pregen string // autofree_vars []AutofreeArgVar // autofree_vars_ids []int } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c076e75df6..e35e68dcef 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -934,7 +934,30 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) { pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type { c.stmts(call_expr.or_block.stmts) - typ := if call_expr.is_method { c.call_method(call_expr) } else { c.call_fn(call_expr) } + // First check everything that applies to both fns and methods + // TODO merge logic from call_method and call_fn + /* + for i, call_arg in call_expr.args { + if call_arg.is_mut { + c.fail_if_immutable(call_arg.expr) + if !arg.is_mut { + tok := call_arg.share.str() + c.error('`$call_expr.name` parameter `$arg.name` is not `$tok`, `$tok` is not needed`', + call_arg.expr.position()) + } else if arg.typ.share() != call_arg.share { + c.error('wrong shared type', call_arg.expr.position()) + } + } else { + if arg.is_mut && (!call_arg.is_mut || arg.typ.share() != call_arg.share) { + tok := call_arg.share.str() + 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()) + } + } + } + */ + // Now call `call_method` or `call_fn` for specific checks. + typ := if call_expr.is_method { c.call_method(mut call_expr) } else { c.call_fn(mut call_expr) } // autofree free_tmp_arg_vars := c.pref.autofree && c.pref.experimental && !c.is_builtin_mod && call_expr.args.len > 0 && !call_expr.args[0].typ.has_flag(.optional) @@ -1153,6 +1176,24 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { call_expr.pos) } } + param := if method.is_variadic && i >= method.args.len - 1 { method.args[method.args.len - + 1] } else { method.args[i + 1] } + if arg.is_mut { + c.fail_if_immutable(arg.expr) + if !param.is_mut { + tok := arg.share.str() + c.error('`$call_expr.name` parameter `$param.name` is not `$tok`, `$tok` is not needed`', + arg.expr.position()) + } else if param.typ.share() != arg.share { + c.error('wrong shared type', arg.expr.position()) + } + } else { + if param.is_mut && (!arg.is_mut || param.typ.share() != arg.share) { + tok := arg.share.str() + c.error('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${i+1}`', + arg.expr.position()) + } + } } if method.is_unsafe && !c.inside_unsafe { c.warn('method `${left_type_sym.source_name}.$method_name` must be called from an `unsafe` block', @@ -2339,7 +2380,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { ast.GotoLabel {} ast.GotoStmt {} ast.HashStmt { - c.hash_stmt(node) + c.hash_stmt(mut node) } ast.Import { c.import_stmt(node) @@ -2358,7 +2399,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { c.scope_returns = true } ast.SqlStmt { - c.sql_stmt(node) + c.sql_stmt(mut node) } ast.StructDecl { c.struct_decl(node) @@ -2663,7 +2704,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { return c.match_expr(mut node) } ast.PostfixExpr { - return c.postfix_expr(node) + return c.postfix_expr(mut node) } ast.PrefixExpr { right_type := c.expr(node.right) @@ -2731,7 +2772,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { return table.u32_type } ast.SqlExpr { - return c.sql_expr(node) + return c.sql_expr(mut node) } ast.StringLiteral { if node.language == .c { @@ -2906,7 +2947,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type { // main.compare_f32 may actually be builtin.compare_f32 saved_mod := ident.mod ident.mod = 'builtin' - builtin_type := c.ident(ident) + builtin_type := c.ident(mut ident) if builtin_type != table.void_type { return builtin_type } @@ -3182,8 +3223,9 @@ pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) table.Type { } pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type { - for id in node.lockeds { - c.ident(mut id) + for i, id in node.lockeds { + // c.ident(mut id) + c.ident(mut node.lockeds[i]) if id.obj is ast.Var as v { if v.typ.share() != .shared_t { c.error('`$id.name` must be declared `shared` to be locked', id.pos) diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index e7b0a01d47..c19fe1b3d1 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -101,6 +101,7 @@ mut: inside_return bool inside_or_block bool strs_to_free []string // strings.Builder + strs_to_free0 []string // strings.Builder inside_call bool has_main bool inside_const bool @@ -1915,7 +1916,21 @@ fn (mut g Gen) expr(node ast.Expr) { g.write(node.val.str()) } ast.CallExpr { + // if g.fileis('1.strings') { + // println('\ncall_expr()()') + // } g.call_expr(node) + // if g.fileis('1.strings') { + // println('before:' + node.autofree_pregen) + // } + if g.pref.autofree && node.autofree_pregen != '' { // g.strs_to_free0.len != 0 { + // g.insert_before_stmt('/*START2*/' + g.strs_to_free0.join('\n') + '/*END*/') + g.insert_before_stmt('/*START3*/' + node.autofree_pregen + '/*END*/') + // for s in g.strs_to_free0 { + // //g.writeln(s) + // } + g.strs_to_free0 = [] + } } ast.CastExpr { // g.write('/*cast*/') diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index 8b947bb8a2..294ee61746 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -419,7 +419,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) { } } } - g.generate_tmp_autofree_arg_vars(node, name) + // TODO2 + // g.generate_tmp_autofree_arg_vars(mut node, name) + // // if node.receiver_type != 0 { // g.write('/*${g.typ(node.receiver_type)}*/') // g.write('/*expr_type=${g.typ(node.left_type)} rec type=${g.typ(node.receiver_type)}*/') @@ -534,7 +536,8 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { // `foo()` => `foo_int()` name += '_' + g.typ(node.generic_type) } - g.generate_tmp_autofree_arg_vars(node, name) + // TODO2 + // g.generate_tmp_autofree_arg_vars(node, name) // Handle `print(x)` if is_print && node.args[0].typ != table.string_type { // && !free_tmp_arg_vars { typ := node.args[0].typ @@ -614,7 +617,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { g.is_json_fn = false } -fn (mut g Gen) generate_tmp_autofree_arg_vars(node ast.CallExpr, name string) { +fn (mut g Gen) generate_tmp_autofree_arg_vars(mut node ast.CallExpr, name string) { + // if g.fileis('1.strings') { + // println('gen tmp autofree()') + // } // Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression, // like `foo(get_string())` or `foo(a + b)` mut free_tmp_arg_vars := g.autofree && g.pref.experimental && !g.is_builtin_mod && @@ -633,7 +639,10 @@ fn (mut g Gen) generate_tmp_autofree_arg_vars(node ast.CallExpr, name string) { t := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i' g.called_fn_name = name str_expr := g.write_expr_to_string(arg.expr) - g.insert_before_stmt('string $t = $str_expr; // new4. to free arg #$i name=$name') + // g.insert_before_stmt('string $t = $str_expr; // new4. to free arg #$i name=$name') + // g.strs_to_free0 << 'string $t = $str_expr; // new5. to free arg #$i name=$name' + node.autofree_pregen += 'string $t = $str_expr; // new6. to free arg #$i name=$name\n' + // println('setting pregen to ' + node.autofree_pregen) // cur_line = g.go_before_stmt(0) // println('cur line ="$cur_line"') // g.writeln('string $t = $str_expr; // new3. to free arg #$i name=$name') diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index eed05f7782..d158af3434 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1501,13 +1501,13 @@ fn (mut p Parser) import_stmt() ast.Import { p.error_with_pos('import alias `$mod_name as $mod_alias` is redundant', p.prev_tok.position()) } } - node := ast.Import{ + mut node := ast.Import{ pos: pos mod: mod_name alias: mod_alias } if p.tok.kind == .lcbr { // import module { fn1, Type2 } syntax - p.import_syms(node) + p.import_syms(mut node) p.register_used_import(mod_name) // no `unused import` msg for parent } pos_t := p.tok.position() diff --git a/vlib/v/tests/valgrind/1.strings_and_arrays.v b/vlib/v/tests/valgrind/1.strings_and_arrays.v index 9f7cc02b25..7d6e0998d2 100644 --- a/vlib/v/tests/valgrind/1.strings_and_arrays.v +++ b/vlib/v/tests/valgrind/1.strings_and_arrays.v @@ -34,6 +34,13 @@ fn str_tmp_expr() { } fn str_tmp_expr_advanced() { + // t1 = 'c' + 'd' + // t2 = 'e + f' + // t3 = add_strings(t2, 'g') + // handle_strings(t1, t3) + // t1.free() + // t2.free() + // t3.free() // handle_strings('c' + 'd', add_strings('e' + 'f', 'g')) // both lvl 1 and lvl2 exprs must be freed } @@ -112,3 +119,11 @@ fn main() { // str_replace() println('end') } + +/* +s := s.replace().replace() +tmp := s.replace() +s.free() +s = tmp.replace() +tmp.free() +*/