diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 1da32ecb35..654c113b11 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -1559,6 +1559,7 @@ fn (t Tree) call_expr(node ast.CallExpr) &Node { obj.add('is_keep_alive', t.bool_node(node.is_keep_alive)) obj.add_terse('is_noreturn', t.bool_node(node.is_noreturn)) obj.add_terse('is_ctor_new', t.bool_node(node.is_ctor_new)) + obj.add_terse('is_return_used', t.bool_node(node.is_return_used)) obj.add('should_be_skipped', t.bool_node(node.should_be_skipped)) obj.add_terse('free_receiver', t.bool_node(node.free_receiver)) obj.add('scope', t.number_node(int(node.scope))) diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index d8e423c51f..fb5ba2d7f9 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -827,6 +827,7 @@ pub mut: scope &Scope = unsafe { nil } from_embed_types []Type // holds the type of the embed that the method is called from comments []Comment + is_return_used bool // return value is used for another expr // is_expand_simple_interpolation bool // true, when the function/method is marked as @[expand_simple_interpolation] // Calls to it with an interpolation argument like `b.f('x ${y}')`, will be converted to `b.f('x ')` followed by `b.f(y)`. diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index 8adf389df2..eaa27480bf 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -580,13 +580,14 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.', right.pos()) c.note('an implicit clone of the slice was done here', right.pos()) right = ast.CallExpr{ - name: 'clone' - left: right - left_type: left_type - is_method: true - receiver_type: left_type - return_type: left_type - scope: c.fn_scope + name: 'clone' + left: right + left_type: left_type + is_method: true + receiver_type: left_type + return_type: left_type + scope: c.fn_scope + is_return_used: true } right_type = c.expr(mut right) node.right[i] = right diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 6897687747..dba551c064 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1371,7 +1371,6 @@ fn (mut c Checker) check_or_last_stmt(mut stmt ast.Stmt, ret_type ast.Type, expr c.expected_type = ret_type c.expected_or_type = ret_type.clear_option_and_result() last_stmt_typ := c.expr(mut stmt.expr) - if last_stmt_typ.has_flag(.option) || last_stmt_typ == ast.none_type { if stmt.expr in [ast.Ident, ast.SelectorExpr, ast.CallExpr, ast.None, ast.CastExpr] { expected_type_name := c.table.type_to_str(ret_type.clear_option_and_result()) diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 592aef3caa..c99a161247 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -733,13 +733,14 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.', init_field.expr.pos()) c.note('an implicit clone of the slice was done here', init_field.expr.pos()) mut right := ast.CallExpr{ - name: 'clone' - left: init_field.expr - left_type: got_type - is_method: true - receiver_type: got_type.ref() - return_type: got_type - scope: c.fn_scope + name: 'clone' + left: init_field.expr + left_type: got_type + is_method: true + receiver_type: got_type.ref() + return_type: got_type + scope: c.fn_scope + is_return_used: true } got_type = c.expr(mut right) node.init_fields[i].expr = right diff --git a/vlib/v/checker/tests/enum_redeclare_err.vv b/vlib/v/checker/tests/enum_redeclare_err.vv index 3cb271170a..3b6fd835fa 100644 --- a/vlib/v/checker/tests/enum_redeclare_err.vv +++ b/vlib/v/checker/tests/enum_redeclare_err.vv @@ -1,9 +1,9 @@ @[flag] pub enum Enum { - a + a } @[flag] pub enum Enum { - a -} \ No newline at end of file + a +} diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 349e7933ed..fea9eabb31 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -1041,7 +1041,7 @@ fn (mut g Gen) gen_cross_var_assign(node &ast.AssignStmt) { g.write(' = *(voidptr*)array_get(') } else { styp := g.styp(info.elem_type) - string_clone := if needs_clone { '/*1*/string_clone(' } else { '' } + string_clone := if needs_clone { 'string_clone(' } else { '' } g.write('${styp} _var_${left.pos.pos} = ${string_clone}*(${styp}*)array_get(') } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 9f29f39a20..8c8276757b 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -2158,7 +2158,11 @@ fn (mut g Gen) stmt(node ast.Stmt) { eprintln('cgen: ${g.file.path:-30} | pos: ${node.pos.line_str():-39} | node: ${ntype} | ${node}') } } + old_inside_call := g.inside_call g.inside_call = false + defer { + g.inside_call = old_inside_call + } if !g.skip_stmt_pos { g.set_current_pos_as_last_stmt_pos() } @@ -7140,6 +7144,10 @@ fn (mut g Gen) gen_or_block_stmts(cvar_name string, cast_typ string, stmts []ast && expr_stmt.expr.or_block.kind == .absent { g.write('${cvar_name} = ') return_wrapped = true + } else if expr_stmt.expr is ast.CallExpr { + if expr_stmt.expr.is_return_used { + g.write('*(${cast_typ}*) ${cvar_name}.data = ') + } } else { g.write('*(${cast_typ}*) ${cvar_name}.data = ') } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index ff301a9750..ab707e4940 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1021,7 +1021,12 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { && unwrapped_styp.starts_with('_v_') { unwrapped_styp = unwrapped_styp[3..] } - g.write('\n ${cur_line} (*(${unwrapped_styp}*)${tmp_opt}.data)') + if node.is_return_used { + // return value is used, so we need to write the unwrapped temporary var + g.write('\n ${cur_line} (*(${unwrapped_styp}*)${tmp_opt}.data)') + } else { + g.write('\n ${cur_line}') + } } else { g.write('\n ${cur_line} ${tmp_opt}') } diff --git a/vlib/v/gen/wasm/gen.v b/vlib/v/gen/wasm/gen.v index 4530f4c709..86d6af75e4 100644 --- a/vlib/v/gen/wasm/gen.v +++ b/vlib/v/gen/wasm/gen.v @@ -631,12 +631,13 @@ pub fn (mut g Gen) call_expr(node ast.CallExpr, expected ast.Type, existing_rvar } expr = ast.CallExpr{ - name: 'str' - left: expr - left_type: typ - receiver_type: typ - return_type: ast.string_type - is_method: true + name: 'str' + left: expr + left_type: typ + receiver_type: typ + return_type: ast.string_type + is_method: true + is_return_used: true } } diff --git a/vlib/v/parser/assign.v b/vlib/v/parser/assign.v index 0655c9a6b5..6d3f17c2b9 100644 --- a/vlib/v/parser/assign.v +++ b/vlib/v/parser/assign.v @@ -9,7 +9,7 @@ fn (mut p Parser) assign_stmt() ast.Stmt { mut defer_vars := p.defer_vars.clone() p.defer_vars = []ast.Ident{} - exprs := p.expr_list() + exprs := p.expr_list(true) if !(p.inside_defer && p.tok.kind == .decl_assign) { defer_vars << p.defer_vars @@ -186,7 +186,10 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr) ast.Stmt { mut pos := p.tok.pos() p.next() mut right := []ast.Expr{cap: left.len} - right = p.expr_list() + old_assign_rhs := p.inside_assign_rhs + p.inside_assign_rhs = true + right = p.expr_list(true) + p.inside_assign_rhs = old_assign_rhs end_comments := p.eat_comments(same_line: true) mut has_cross_var := false mut is_static := false diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index d49d759e0f..7b5a8094bf 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -15,7 +15,26 @@ fn (mut p Parser) check_expr_level() ! { } } +fn (mut p Parser) expr_no_value(precedence int) ast.Expr { + old_expecting_value := p.expecting_value + p.expecting_value = false + defer { + p.expecting_value = old_expecting_value + } + return p.check_expr(precedence) or { + if token.is_decl(p.tok.kind) && p.disallow_declarations_in_script_mode() { + return ast.empty_expr + } + p.unexpected(prepend_msg: 'invalid expression:') + } +} + fn (mut p Parser) expr(precedence int) ast.Expr { + old_expecting_value := p.expecting_value + p.expecting_value = true + defer { + p.expecting_value = old_expecting_value + } return p.check_expr(precedence) or { if token.is_decl(p.tok.kind) && p.disallow_declarations_in_script_mode() { return ast.empty_expr @@ -466,12 +485,13 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr { p.check(.rpar) or_block := p.gen_or_block() node = ast.CallExpr{ - name: 'anon' - left: node - args: args - pos: pos - or_block: or_block - scope: p.scope + name: 'anon' + left: node + args: args + pos: pos + or_block: or_block + scope: p.scope + is_return_used: p.expecting_value } } return node @@ -572,11 +592,12 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo p.check(.rpar) or_block := p.gen_or_block() node = ast.CallExpr{ - left: node - args: args - pos: pos - scope: p.scope - or_block: or_block + left: node + args: args + pos: pos + scope: p.scope + or_block: or_block + is_return_used: p.expecting_value } p.is_stmt_ident = is_stmt_ident if p.tok.kind == .lpar && p.prev_tok.line_nr == p.tok.line_nr { @@ -613,7 +634,10 @@ fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bo tok := p.tok mut pos := tok.pos() p.next() + old_assign_rhs := p.inside_assign_rhs + p.inside_assign_rhs = true right := p.expr(precedence - 1) + p.inside_assign_rhs = old_assign_rhs pos.update_last_line(p.prev_tok.line_nr) if mut node is ast.IndexExpr { node.recursive_arraymap_set_is_setter() @@ -757,7 +781,10 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { } right_op_pos := p.tok.pos() + old_assign_rhs := p.inside_assign_rhs + p.inside_assign_rhs = true right = p.expr(precedence) + p.inside_assign_rhs = old_assign_rhs if op in [.plus, .minus, .mul, .div, .mod, .lt, .eq] && mut right is ast.PrefixExpr { mut right_expr := right.right for mut right_expr is ast.ParExpr { diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 0aee74446b..364f3c7231 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -111,11 +111,12 @@ fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr { } scope: p.scope comments: comments + is_return_used: p.expecting_value } } fn (mut p Parser) call_args() []ast.CallArg { - prev_inside_call_args := true + prev_inside_call_args := p.inside_call_args p.inside_call_args = true defer { p.inside_call_args = prev_inside_call_args @@ -1168,7 +1169,8 @@ fn (mut p Parser) spawn_expr() ast.SpawnExpr { } else { p.error_with_pos('expression in `spawn` must be a function call', expr.pos()) ast.CallExpr{ - scope: p.scope + scope: p.scope + is_return_used: true } } pos := spos.extend(p.prev_tok.pos()) @@ -1189,7 +1191,8 @@ fn (mut p Parser) go_expr() ast.GoExpr { } else { p.error_with_pos('expression in `go` must be a function call', expr.pos()) ast.CallExpr{ - scope: p.scope + scope: p.scope + is_return_used: true } } pos := spos.extend(p.prev_tok.pos()) diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index b3ebf616d2..d395370d51 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -170,6 +170,13 @@ fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { } p.open_scope() stmts := p.parse_block_no_scope(false) + // if the last expr is a callexpr mark its return as used + if p.inside_assign_rhs && stmts.len > 0 && stmts.last() is ast.ExprStmt { + mut last_expr := stmts.last() as ast.ExprStmt + if mut last_expr.expr is ast.CallExpr { + last_expr.expr.is_return_used = true + } + } branches << ast.IfBranch{ cond: cond stmts: stmts @@ -245,11 +252,12 @@ fn (mut p Parser) is_match_sumtype_type() bool { fn (mut p Parser) match_expr() ast.MatchExpr { match_first_pos := p.tok.pos() + old_inside_match := p.inside_match p.inside_match = true p.check(.key_match) mut is_sum_type := false cond := p.expr(0) - p.inside_match = false + p.inside_match = old_inside_match no_lcbr := p.tok.kind != .lcbr if !no_lcbr { p.check(.lcbr) @@ -353,6 +361,12 @@ fn (mut p Parser) match_expr() ast.MatchExpr { pos := branch_first_pos.extend_with_last_line(branch_last_pos, p.prev_tok.line_nr) branch_pos := branch_first_pos.extend_with_last_line(p.tok.pos(), p.tok.line_nr) post_comments := p.eat_comments() + if p.inside_assign_rhs && stmts.len > 0 && stmts.last() is ast.ExprStmt { + mut last_expr := stmts.last() as ast.ExprStmt + if mut last_expr.expr is ast.CallExpr { + last_expr.expr.is_return_used = true + } + } branches << ast.MatchBranch{ exprs: exprs ecmnts: ecmnts @@ -430,7 +444,7 @@ fn (mut p Parser) select_expr() ast.SelectExpr { } p.inside_match = true p.inside_select = true - exprs := p.expr_list() + exprs := p.expr_list(true) if exprs.len != 1 { p.error('only one expression allowed as `select` key') return ast.SelectExpr{} diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 5b2eec56fd..d2945969c7 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -54,6 +54,7 @@ mut: inside_array_lit bool inside_in_array bool inside_infix bool + inside_assign_rhs bool // rhs assignment inside_match bool // to separate `match A { }` from `Struct{}` inside_select bool // to allow `ch <- Struct{} {` inside `select` inside_match_case bool // to separate `match_expr { }` from `Struct{}` @@ -90,6 +91,7 @@ mut: returns bool is_stmt_ident bool // true while the beginning of a statement is an ident/selector expecting_type bool // `is Type`, expecting type + expecting_value bool = true // true where a node value will be used cur_fn_name string cur_fn_scope &ast.Scope = unsafe { nil } label_names []string @@ -1857,10 +1859,10 @@ fn (mut p Parser) asm_ios(output bool) []ast.AsmIO { return res } -fn (mut p Parser) expr_list() []ast.Expr { +fn (mut p Parser) expr_list(expect_value bool) []ast.Expr { mut exprs := []ast.Expr{} for { - expr := p.expr(0) + expr := if expect_value { p.expr(0) } else { p.expr_no_value(0) } if expr !is ast.Comment { exprs << expr if p.tok.kind != .comma { @@ -2228,7 +2230,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt { mut defer_vars := p.defer_vars.clone() p.defer_vars = []ast.Ident{} - left := p.expr_list() + left := p.expr_list(p.inside_assign_rhs) if !(p.inside_defer && p.tok.kind == .decl_assign) { defer_vars << p.defer_vars @@ -2881,11 +2883,12 @@ fn (mut p Parser) name_expr() ast.Expr { p.check(.rpar) or_block := p.gen_or_block() node = ast.CallExpr{ - left: node - args: args - pos: pos - scope: p.scope - or_block: or_block + left: node + args: args + pos: pos + scope: p.scope + or_block: or_block + is_return_used: p.expecting_value } } } @@ -3325,6 +3328,10 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { end_pos := p.prev_tok.pos() pos := name_pos.extend(end_pos) comments := p.eat_comments(same_line: true) + mut left_node := unsafe { left } + if mut left_node is ast.CallExpr { + left_node.is_return_used = true + } mcall_expr := ast.CallExpr{ left: left name: field_name @@ -3337,6 +3344,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { or_block: or_block scope: p.scope comments: comments + is_return_used: p.expecting_value } return mcall_expr } @@ -3382,7 +3390,10 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { scope: p.scope next_token: p.tok.kind } - + mut left_node := unsafe { left } + if mut left_node is ast.CallExpr { + left_node.is_return_used = true + } return sel_expr } @@ -3984,7 +3995,10 @@ fn (mut p Parser) return_stmt() ast.Return { } } // return exprs - exprs := p.expr_list() + old_assign_rhs := p.inside_assign_rhs + p.inside_assign_rhs = true + exprs := p.expr_list(true) + p.inside_assign_rhs = old_assign_rhs end_pos := exprs.last().pos() return ast.Return{ exprs: exprs