cgen: remove unused code generated for unwrapping temp var from callexpr (detect unused return value from CallExpr), fix parser bugs (#22769)

This commit is contained in:
Felipe Pena 2024-11-07 07:01:33 -03:00 committed by GitHub
parent 505a247706
commit dab25cad49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 132 additions and 54 deletions

View File

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

View File

@ -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)`.

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
@[flag]
pub enum Enum {
a
a
}
@[flag]
pub enum Enum {
a
a
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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