diff --git a/vlib/v/gen/c/assert.v b/vlib/v/gen/c/assert.v index 46b48f7f00..5779177090 100644 --- a/vlib/v/gen/c/assert.v +++ b/vlib/v/gen/c/assert.v @@ -12,19 +12,11 @@ fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) { mut node := original_assert_statement g.writeln('// assert') if mut node.expr is ast.InfixExpr { - if mut node.expr.left is ast.CallExpr { - node.expr.left = g.new_ctemp_var_then_gen(node.expr.left, node.expr.left_type) - } else if mut node.expr.left is ast.ParExpr { - if node.expr.left.expr is ast.CallExpr { - node.expr.left = g.new_ctemp_var_then_gen(node.expr.left.expr, node.expr.left_type) - } + if subst_expr := g.assert_subexpression_to_ctemp(node.expr.left, node.expr.left_type) { + node.expr.left = subst_expr } - if mut node.expr.right is ast.CallExpr { - node.expr.right = g.new_ctemp_var_then_gen(node.expr.right, node.expr.right_type) - } else if mut node.expr.right is ast.ParExpr { - if node.expr.right.expr is ast.CallExpr { - node.expr.right = g.new_ctemp_var_then_gen(node.expr.right.expr, node.expr.right_type) - } + if subst_expr := g.assert_subexpression_to_ctemp(node.expr.right, node.expr.right_type) { + node.expr.right = subst_expr } } g.inside_ternary++ @@ -60,6 +52,39 @@ fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) { } } +struct UnsupportedAssertCtempTransform { + msg string + code int +} + +const unsupported_ctemp_assert_transform = IError(UnsupportedAssertCtempTransform{}) + +fn (mut g Gen) assert_subexpression_to_ctemp(expr ast.Expr, expr_type ast.Type) ?ast.Expr { + match expr { + ast.CallExpr { + return g.new_ctemp_var_then_gen(expr, expr_type) + } + ast.ParExpr { + if expr.expr is ast.CallExpr { + return g.new_ctemp_var_then_gen(expr.expr, expr_type) + } + } + ast.SelectorExpr { + if expr.expr is ast.CallExpr { + sym := g.table.get_final_type_symbol(g.unwrap_generic(expr.expr.return_type)) + if sym.kind == .struct_ { + if (sym.info as ast.Struct).is_union { + return c.unsupported_ctemp_assert_transform + } + } + return g.new_ctemp_var_then_gen(expr, expr_type) + } + } + else {} + } + return c.unsupported_ctemp_assert_transform +} + fn (mut g Gen) gen_assert_postfailure_mode(node ast.AssertStmt) { g.write_v_source_line_info(node.pos) match g.pref.assert_failure_mode { diff --git a/vlib/v/gen/c/testdata/assert_fncalls.c.must_have b/vlib/v/gen/c/testdata/assert_fncalls.c.must_have new file mode 100644 index 0000000000..ec6c6e46e6 --- /dev/null +++ b/vlib/v/gen/c/testdata/assert_fncalls.c.must_have @@ -0,0 +1,9 @@ +int _t1 = main__fn_returning_array().len; +if (!(_t1 == 1)) { +v_assert_meta_info__t2.src = _SLIT("fn_returning_array().len == 1"); +v_assert_meta_info__t2.llabel = _SLIT("fn_returning_array().len"); + +int _t3 = main__fn_returning_struct().x; +if (!(_t3 == 123)) { +v_assert_meta_info__t4.src = _SLIT("fn_returning_struct().x == 123"); +v_assert_meta_info__t4.llabel = _SLIT("fn_returning_struct().x"); diff --git a/vlib/v/gen/c/testdata/assert_fncalls.out b/vlib/v/gen/c/testdata/assert_fncalls.out new file mode 100644 index 0000000000..04bbe63a4d --- /dev/null +++ b/vlib/v/gen/c/testdata/assert_fncalls.out @@ -0,0 +1,4 @@ +[1] +Abc{ + x: 123 +} diff --git a/vlib/v/gen/c/testdata/assert_fncalls.vv b/vlib/v/gen/c/testdata/assert_fncalls.vv new file mode 100644 index 0000000000..ae554f522d --- /dev/null +++ b/vlib/v/gen/c/testdata/assert_fncalls.vv @@ -0,0 +1,18 @@ +struct Abc { + x int +} + +fn fn_returning_array() []int { + return [1] +} + +fn fn_returning_struct() Abc { + return Abc{123} +} + +fn main() { + assert fn_returning_array().len == 1 + assert fn_returning_struct().x == 123 + println(fn_returning_array()) + println(fn_returning_struct()) +} diff --git a/vlib/v/gen/js/sourcemap/source_map.v b/vlib/v/gen/js/sourcemap/source_map.v index 44f79d1e86..bf864e13ae 100644 --- a/vlib/v/gen/js/sourcemap/source_map.v +++ b/vlib/v/gen/js/sourcemap/source_map.v @@ -39,7 +39,9 @@ pub fn new_sourcemap(file string, source_root string, sources_content_inline boo // Add a single mapping from original source line and column to the generated source's line and column for this source map being created. pub fn (mut sm SourceMap) add_mapping(source_name string, source_position SourcePositionType, gen_line u32, gen_column u32, name string) { - assert source_name.len != 0 + if source_name.len == 0 { + panic('add_mapping, source_name should not be ""') + } sources_ind := sm.sources.add(source_name) @@ -53,7 +55,9 @@ pub fn (mut sm SourceMap) add_mapping(source_name string, source_position Source // Add multiple mappings from the same source pub fn (mut sm SourceMap) add_mapping_list(source_name string, mapping_list []MappingInput) ? { - assert source_name.len != 0 + if source_name.len == 0 { + panic('add_mapping_list, source_name should not be ""') + } sources_ind := sm.sources.add(source_name) diff --git a/vlib/v/tests/assert_should_evaluate_args_just_once_test.v b/vlib/v/tests/assert_should_evaluate_args_just_once_test.v new file mode 100644 index 0000000000..5a2d8a1887 --- /dev/null +++ b/vlib/v/tests/assert_should_evaluate_args_just_once_test.v @@ -0,0 +1,39 @@ +struct Abc { + ncalls int +} + +[unsafe] +fn fn_that_should_be_called_just_once_array() []int { + mut static ncalls := 0 + ncalls++ + println('${@FN} calls: $ncalls') + if ncalls > 1 { + assert false + } + return []int{len: ncalls, init: ncalls} +} + +[unsafe] +fn fn_that_should_be_called_just_once_struct() Abc { + mut static ncalls := 0 + ncalls++ + println('${@FN} calls: $ncalls') + if ncalls > 1 { + assert false + } + return Abc{ + ncalls: ncalls + } +} + +fn test_assert_calls_a_function_returning_an_array_just_once() { + unsafe { + assert fn_that_should_be_called_just_once_array().len == 1 + } +} + +fn test_assert_calls_a_function_returning_a_struct_just_once() { + unsafe { + assert fn_that_should_be_called_just_once_struct().ncalls == 1 + } +}