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