v.gen.c: fix assert `f().len == 1` calling f() twice (closes issue 11501)
							parent
							
								
									c175b4fd48
								
							
						
					
					
						commit
						0a18690a4f
					
				| 
						 | 
					@ -12,19 +12,11 @@ fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) {
 | 
				
			||||||
	mut node := original_assert_statement
 | 
						mut node := original_assert_statement
 | 
				
			||||||
	g.writeln('// assert')
 | 
						g.writeln('// assert')
 | 
				
			||||||
	if mut node.expr is ast.InfixExpr {
 | 
						if mut node.expr is ast.InfixExpr {
 | 
				
			||||||
		if mut node.expr.left is ast.CallExpr {
 | 
							if subst_expr := g.assert_subexpression_to_ctemp(node.expr.left, node.expr.left_type) {
 | 
				
			||||||
			node.expr.left = g.new_ctemp_var_then_gen(node.expr.left, node.expr.left_type)
 | 
								node.expr.left = subst_expr
 | 
				
			||||||
		} 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 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++
 | 
						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) {
 | 
					fn (mut g Gen) gen_assert_postfailure_mode(node ast.AssertStmt) {
 | 
				
			||||||
	g.write_v_source_line_info(node.pos)
 | 
						g.write_v_source_line_info(node.pos)
 | 
				
			||||||
	match g.pref.assert_failure_mode {
 | 
						match g.pref.assert_failure_mode {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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");
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					[1]
 | 
				
			||||||
 | 
					Abc{
 | 
				
			||||||
 | 
					    x: 123
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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.
 | 
					// 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) {
 | 
					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)
 | 
						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
 | 
					// Add multiple mappings from the same source
 | 
				
			||||||
pub fn (mut sm SourceMap) add_mapping_list(source_name string, mapping_list []MappingInput) ? {
 | 
					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)
 | 
						sources_ind := sm.sources.add(source_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue