From 52fb7033c34be49751c0c2c8c9e57df06357d19f Mon Sep 17 00:00:00 2001 From: spaceface777 Date: Fri, 4 Dec 2020 19:29:34 +0100 Subject: [PATCH] gen: fix evaluating the receiver of array methods (.map(), .filter(), etc) more than once (#7130) --- vlib/v/gen/cgen.v | 19 ++++++++----------- vlib/v/tests/array_methods_test.v | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 vlib/v/tests/array_methods_test.v diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index e12e262801..164fa3be6e 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -4767,16 +4767,14 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) { if inp_sym.kind != .array { verror('map() requires an array') } - g.writeln('') - g.write('int ${tmp}_len = ') + g.write('${g.typ(node.left_type)} ${tmp}_orig = ') g.expr(node.left) - g.writeln('.len;') + g.writeln(';') + g.write('int ${tmp}_len = ${tmp}_orig.len;') g.writeln('$ret_typ $tmp = __new_array(0, ${tmp}_len, sizeof($ret_elem_type));') i := g.new_tmp_var() g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') - g.write('\t$inp_elem_type it = (($inp_elem_type*) ') - g.expr(node.left) - g.writeln('.data)[$i];') + g.write('\t$inp_elem_type it = (($inp_elem_type*) ${tmp}_orig.data)[$i];') g.write('\t$ret_elem_type ti = ') expr := node.args[0].expr match expr { @@ -4919,14 +4917,13 @@ fn (mut g Gen) gen_array_filter(node ast.CallExpr) { info := sym.info as table.Array styp := g.typ(node.return_type) elem_type_str := g.typ(info.elem_type) - g.write('\nint ${tmp}_len = ') + g.write('${g.typ(node.left_type)} ${tmp}_orig = ') g.expr(node.left) - g.writeln('.len;') + g.writeln(';') + g.write('int ${tmp}_len = ${tmp}_orig.len;') g.writeln('$styp $tmp = __new_array(0, ${tmp}_len, sizeof($elem_type_str));') g.writeln('for (int i = 0; i < ${tmp}_len; ++i) {') - g.write(' $elem_type_str it = (($elem_type_str*) ') - g.expr(node.left) - g.writeln('.data)[i];') + g.writeln(' $elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[i];') g.write('if (') expr := node.args[0].expr match expr { diff --git a/vlib/v/tests/array_methods_test.v b/vlib/v/tests/array_methods_test.v new file mode 100644 index 0000000000..19dd2cbfa7 --- /dev/null +++ b/vlib/v/tests/array_methods_test.v @@ -0,0 +1,20 @@ +struct Counter { +mut: + val int +} + +// if this is called more than once, the test'll fail +fn (mut c Counter) new_arr(msg string) []int { + if c.val > 0 { panic(msg) } + c.val++ + return [1, 3, 2] +} + +fn test_array_eval_count() { + // `new_arr()` should only be evaluated once, not on every iteration + mut a1 := Counter{} + assert a1.new_arr('map() failed').map(it * 2) == [2, 6, 4] + + mut a2 := Counter{} + assert a2.new_arr('filter() failed').filter(it < 3) == [1, 2] +}