From 57cc00df222d3a8921f37c180a93d485b4bf2976 Mon Sep 17 00:00:00 2001 From: shove Date: Thu, 28 Dec 2023 18:47:45 +0800 Subject: [PATCH] cgen: fix generating callexpr to string is actually called twice (fix #20282) (#20288) --- vlib/v/gen/c/str.v | 30 +++++++++++++++++-- ...xpr_to_string_with_call_and_return_ref.out | 4 +++ ...expr_to_string_with_call_and_return_ref.vv | 18 +++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 vlib/v/gen/c/testdata/gen_expr_to_string_with_call_and_return_ref.out create mode 100644 vlib/v/gen/c/testdata/gen_expr_to_string_with_call_and_return_ref.vv diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v index a8a2f92a6..82376de02 100644 --- a/vlib/v/gen/c/str.v +++ b/vlib/v/gen/c/str.v @@ -111,12 +111,28 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { is_dump_expr := expr is ast.DumpExpr is_var_mut := expr.is_auto_deref_var() str_fn_name := g.get_str_fn(exp_typ) + temp_var_needed := expr is ast.CallExpr && expr.return_type.is_ptr() + mut tmp_var := '' + if temp_var_needed { + tmp_var = g.new_tmp_var() + ret_typ := g.typ(exp_typ) + line := g.go_before_last_stmt().trim_space() + g.empty_line = true + g.write('${ret_typ} ${tmp_var} = ') + g.expr(expr) + g.writeln(';') + g.write(line) + } if is_ptr && !is_var_mut { ref_str := '&'.repeat(typ.nr_muls()) g.write('str_intp(1, _MOV((StrIntpData[]){{_SLIT("${ref_str}"), ${si_s_code} ,{.d_s = isnil(') if typ.has_flag(.option) { g.write('*(${g.base_type(exp_typ)}*)&') - g.expr(expr) + if temp_var_needed { + g.write(tmp_var) + } else { + g.expr(expr) + } g.write('.data') g.write(') ? _SLIT("Option(&nil)") : ') } else { @@ -125,7 +141,11 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { defer { g.inside_interface_deref = inside_interface_deref_old } - g.expr(expr) + if temp_var_needed { + g.write(tmp_var) + } else { + g.expr(expr) + } g.write(') ? _SLIT("nil") : ') } } @@ -155,7 +175,11 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { if unwrap_option { g.expr(expr) } else { - g.expr_with_cast(expr, typ, typ) + if temp_var_needed { + g.write(tmp_var) + } else { + g.expr_with_cast(expr, typ, typ) + } } if is_shared { diff --git a/vlib/v/gen/c/testdata/gen_expr_to_string_with_call_and_return_ref.out b/vlib/v/gen/c/testdata/gen_expr_to_string_with_call_and_return_ref.out new file mode 100644 index 000000000..f01a73fdd --- /dev/null +++ b/vlib/v/gen/c/testdata/gen_expr_to_string_with_call_and_return_ref.out @@ -0,0 +1,4 @@ +It should be printed only once +&Abc{ + s: '' +} diff --git a/vlib/v/gen/c/testdata/gen_expr_to_string_with_call_and_return_ref.vv b/vlib/v/gen/c/testdata/gen_expr_to_string_with_call_and_return_ref.vv new file mode 100644 index 000000000..20c6fbbcf --- /dev/null +++ b/vlib/v/gen/c/testdata/gen_expr_to_string_with_call_and_return_ref.vv @@ -0,0 +1,18 @@ +// for issue 20282 +// Phenomenon of issue: +// When the expression is CallExpr and the return type is a pointer, cgen generates 2 actual call actions: +// V code: println(call_test()) +// C code: +// println(str_intp(1, _MOV((StrIntpData[]){{_SLIT("&"), 0xfe10 ,{.d_s = isnil(call_test()) ? _SLIT("nil") : string_str(*call_test())}}}))); +struct Abc { + s string +} + +fn str_gen_with_call_and_return_ref() &Abc { + println('It should be printed only once') + return &Abc{} +} + +fn main() { + println(str_gen_with_call_and_return_ref()) +} -- 2.39.5