v4 / vlib / v / gen / c / assert.v
362 lines · 349 sloc · 11.05 KB · 98bbdd7e80922d81897461dc303b889be2c68980
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module c
5
6import v.ast
7
8enum AssertMetainfoKind {
9 pass
10 fail
11 panic
12}
13
14fn (mut g Gen) assert_stmt(original_assert_statement ast.AssertStmt) {
15 if !original_assert_statement.is_used {
16 return
17 }
18 mut node := original_assert_statement
19 g.writeln('// assert')
20
21 mut save_left := ast.empty_expr
22 mut save_right := ast.empty_expr
23
24 if mut node.expr is ast.InfixExpr {
25 // Don't extract subexpressions to ctemps for && and || operators,
26 // as this would break short-circuit evaluation semantics.
27 // The right side of && must only execute after the left side is true.
28 if node.expr.op !in [.and, .logical_or] {
29 mut left_expr_type := node.expr.left_type
30 mut right_expr_type := node.expr.right_type
31 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
32 resolved_left_type := g.resolved_expr_type(node.expr.left, node.expr.left_type)
33 if resolved_left_type != 0 {
34 resolved_sym := g.table.sym(resolved_left_type)
35 left_sym := g.table.sym(left_expr_type)
36 if resolved_sym.kind !in [.sum_type, .interface]
37 || left_sym.kind in [.sum_type, .interface] {
38 left_expr_type = resolved_left_type
39 }
40 }
41 resolved_right_type := g.resolved_expr_type(node.expr.right, node.expr.right_type)
42 if resolved_right_type != 0 {
43 resolved_sym := g.table.sym(resolved_right_type)
44 right_sym := g.table.sym(right_expr_type)
45 if resolved_sym.kind !in [.sum_type, .interface]
46 || right_sym.kind in [.sum_type, .interface] {
47 right_expr_type = resolved_right_type
48 }
49 }
50 }
51 if subst_expr := g.assert_subexpression_to_ctemp(node.expr.left, left_expr_type) {
52 save_left = node.expr.left
53 node.expr.left = subst_expr
54 }
55 // For || and && operators, do not pre-evaluate the right side
56 // to allow short-circuit evaluation to work correctly.
57 if node.expr.op !in [.logical_or, .and] {
58 if subst_expr := g.assert_subexpression_to_ctemp(node.expr.right, right_expr_type) {
59 save_right = node.expr.right
60 node.expr.right = subst_expr
61 }
62 }
63 }
64 }
65 metaname := g.gen_assert_metainfo_common(node)
66 g.set_current_pos_as_last_stmt_pos()
67 g.inside_ternary++
68 if g.pref.is_test {
69 g.write('if (')
70 prev_inside_ternary := g.inside_ternary
71 g.inside_ternary = 0
72 g.expr(node.expr)
73 g.inside_ternary = prev_inside_ternary
74 g.write(')')
75 g.decrement_inside_ternary()
76 g.writeln(' {')
77 g.gen_assert_metainfo(node, .pass, metaname)
78 g.writeln('\tmain__TestRunner_name_table[test_runner._typ]._method_assert_pass(test_runner._object, &${metaname});')
79 g.writeln('} else {')
80 g.gen_assert_metainfo(node, .fail, metaname)
81 g.writeln('\tmain__TestRunner_name_table[test_runner._typ]._method_assert_fail(test_runner._object, &${metaname});')
82 g.gen_assert_postfailure_mode(node)
83 g.writeln('}')
84 } else {
85 g.write('if (!(')
86 prev_inside_ternary := g.inside_ternary
87 g.inside_ternary = 0
88 g.expr(node.expr)
89 g.inside_ternary = prev_inside_ternary
90 g.write('))')
91 g.decrement_inside_ternary()
92 g.writeln(' {')
93 g.gen_assert_metainfo(node, .panic, metaname)
94 g.writeln('\tbuiltin____print_assert_failure(&${metaname});')
95 g.gen_assert_postfailure_mode(node)
96 g.writeln('}')
97 }
98
99 if mut node.expr is ast.InfixExpr {
100 restore_left := node.expr.left is ast.CTempVar
101 restore_right := node.expr.right is ast.CTempVar
102 if restore_left {
103 node.expr.left = save_left
104 }
105 if restore_right {
106 node.expr.right = save_right
107 }
108 }
109}
110
111fn (mut g Gen) assert_subexpression_to_ctemp(expr ast.Expr, expr_type ast.Type) ?ast.Expr {
112 match expr {
113 ast.CallExpr {
114 return g.new_ctemp_var_then_gen(expr, expr_type)
115 }
116 ast.ParExpr {
117 if expr.expr is ast.CallExpr {
118 return g.new_ctemp_var_then_gen(ast.Expr(expr.expr), expr_type)
119 }
120 }
121 ast.PostfixExpr {
122 return g.new_ctemp_var_then_gen(expr, expr_type)
123 }
124 ast.SelectorExpr {
125 if expr.expr is ast.AsCast {
126 mut subst_expr := expr
127 as_cast_expr := expr.expr as ast.AsCast
128 subst_expr.expr = ast.Expr(g.new_ctemp_var_then_gen(ast.Expr(as_cast_expr),
129 as_cast_expr.typ))
130 return ast.Expr(subst_expr)
131 }
132 if expr.expr is ast.ParExpr && expr.expr.expr is ast.AsCast {
133 mut subst_expr := expr
134 as_cast_expr := expr.expr.expr as ast.AsCast
135 subst_expr.expr = ast.Expr(g.new_ctemp_var_then_gen(ast.Expr(as_cast_expr),
136 as_cast_expr.typ))
137 return ast.Expr(subst_expr)
138 }
139 if expr.expr is ast.CallExpr {
140 sym := g.table.final_sym(g.unwrap_generic(expr.expr.return_type))
141 if sym.kind == .struct {
142 if (sym.info as ast.Struct).is_union {
143 return none
144 }
145 }
146 return g.new_ctemp_var_then_gen(expr, expr_type)
147 }
148 if g.need_tmp_var_in_expr(expr) {
149 return g.new_ctemp_var_then_gen(expr, expr_type)
150 }
151 }
152 ast.LockExpr {
153 return g.new_ctemp_var_then_gen(expr, expr_type)
154 }
155 ast.ArrayInit {
156 if expr.has_callexpr || g.need_tmp_var_in_expr(expr) {
157 return g.new_ctemp_var_then_gen(expr, expr_type)
158 }
159 }
160 ast.StructInit {
161 if g.need_tmp_var_in_expr(expr) {
162 return g.new_ctemp_var_then_gen(expr, expr_type)
163 }
164 }
165 else {
166 if g.need_tmp_var_in_expr(expr) {
167 return g.new_ctemp_var_then_gen(expr, expr_type)
168 }
169 }
170 }
171
172 return none
173}
174
175fn (mut g Gen) gen_assert_postfailure_mode(node ast.AssertStmt) {
176 g.write_v_source_line_info_stmt(node)
177 if g.pref.assert_failure_mode == .continues
178 || g.fn_decl.attrs.any(it.name == 'assert_continues') {
179 return
180 }
181 if g.pref.is_test && !g.inside_defer_generation && g.cur_fn != unsafe { nil }
182 && g.cur_fn.scope != unsafe { nil } {
183 g.write_defer_stmts_when_needed(g.innermost_active_defer_scope(node.pos), true, node.pos)
184 }
185 if g.pref.assert_failure_mode == .aborts || g.fn_decl.attrs.any(it.name == 'assert_aborts') {
186 g.writeln('\tabort();')
187 }
188 if g.pref.assert_failure_mode == .backtraces
189 || g.fn_decl.attrs.any(it.name == 'assert_backtraces') {
190 if _ := g.table.fns['print_backtrace'] {
191 g.writeln('\tbuiltin__print_backtrace();')
192 }
193 }
194 if g.pref.is_test {
195 g.writeln('\tlongjmp(g_jump_buffer, 1);')
196 }
197 if g.pref.assert_failure_mode != .continues {
198 g.writeln('\tbuiltin___v_panic(_S("Assertion failed..."));')
199 }
200}
201
202fn (mut g Gen) gen_assert_metainfo(node ast.AssertStmt, kind AssertMetainfoKind, metaname string) {
203 if kind == .pass {
204 return
205 }
206 if node.extra is ast.EmptyExpr {
207 g.writeln('\t${metaname}.has_msg = false;')
208 g.writeln('\t${metaname}.message = _SLIT0;')
209 } else {
210 g.writeln('\t${metaname}.has_msg = true;')
211 g.write('\t${metaname}.message = ')
212 g.gen_assert_single_expr(node.extra, ast.string_type)
213 g.writeln(';')
214 }
215 match node.expr {
216 ast.InfixExpr {
217 mut left_type := if node.expr.left_ct_expr {
218 g.type_resolver.get_type_or_default(node.expr.left, node.expr.left_type)
219 } else {
220 node.expr.left_type
221 }
222 mut right_type := if node.expr.right_ct_expr {
223 g.type_resolver.get_type(node.expr.right)
224 } else {
225 node.expr.right_type
226 }
227 if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 {
228 resolved_left := g.resolved_expr_type(node.expr.left, node.expr.left_type)
229 if resolved_left != 0 {
230 resolved_sym := g.table.sym(resolved_left)
231 left_sym := g.table.sym(left_type)
232 if resolved_sym.kind !in [.sum_type, .interface]
233 || left_sym.kind in [.sum_type, .interface] {
234 left_type = resolved_left
235 }
236 }
237 resolved_right := g.resolved_expr_type(node.expr.right, node.expr.right_type)
238 if resolved_right != 0 {
239 resolved_sym := g.table.sym(resolved_right)
240 right_sym := g.table.sym(right_type)
241 if resolved_sym.kind !in [.sum_type, .interface]
242 || right_sym.kind in [.sum_type, .interface] {
243 right_type = resolved_right
244 }
245 }
246 }
247 g.write('\t${metaname}.lvalue = ')
248 g.gen_assert_single_expr(node.expr.left, left_type)
249 g.writeln(';')
250 // For && and || operators, use a placeholder for rvalue
251 // to avoid issues with go_before_last_stmt() which can generate
252 // temporary variables outside the conditional scope.
253 // - For &&: if left is false, right is short-circuited
254 // - For ||: if left is true, right is short-circuited
255 if node.expr.op in [.logical_or, .and] {
256 g.writeln('\t${metaname}.rvalue = _S("<short-circuited>");')
257 } else {
258 g.write('\t${metaname}.rvalue = ')
259 g.gen_assert_single_expr(node.expr.right, right_type)
260 g.writeln(';')
261 }
262 }
263 else {}
264 }
265}
266
267fn (mut g Gen) gen_assert_metainfo_common(node ast.AssertStmt) string {
268 mod_path := cestring(g.file.path)
269 fn_name := g.fn_decl.name
270 line_nr := node.pos.line_nr
271 mut src := node.expr.str()
272 if node.extra !is ast.EmptyExpr {
273 src += ', ' + node.extra.str()
274 }
275 src = cestring(src)
276 metaname := 'v_assert_meta_info_${g.new_tmp_var()}'
277 g.writeln('VAssertMetaInfo ${metaname} = {0};')
278 g.writeln('${metaname}.fpath = ${ctoslit(mod_path)};')
279 g.writeln('${metaname}.line_nr = ${line_nr};')
280 g.writeln('${metaname}.fn_name = ${ctoslit(fn_name)};')
281 metasrc := cnewlines(ctoslit(src))
282 g.writeln('${metaname}.src = ${metasrc};')
283 g.writeln('${metaname}.has_msg = false;')
284 match node.expr {
285 ast.InfixExpr {
286 expr_op_str := ctoslit(node.expr.op.str())
287 expr_left_str := cnewlines(ctoslit(node.expr.left.str()))
288 expr_right_str := cnewlines(ctoslit(node.expr.right.str()))
289 g.writeln('${metaname}.op = ${expr_op_str};')
290 g.writeln('${metaname}.llabel = ${expr_left_str};')
291 g.writeln('${metaname}.rlabel = ${expr_right_str};')
292 }
293 ast.CallExpr {
294 g.writeln('${metaname}.op = _S("call");')
295 }
296 else {}
297 }
298
299 return metaname
300}
301
302fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) {
303 // eprintln('> gen_assert_single_expr typ: ${typ} | expr: ${expr} | typeof(expr): ${typeof(expr)}')
304 expr_str := '${expr}'
305 match expr {
306 ast.CastExpr {
307 if typ.is_float() || g.table.final_sym(typ).is_float() {
308 g.gen_expr_to_string(expr.expr, typ)
309 } else {
310 g.write(ctoslit(expr_str))
311 }
312 }
313 ast.ParExpr {
314 g.gen_assert_single_expr(expr.expr, typ)
315 }
316 ast.IfExpr, ast.MatchExpr, ast.RangeExpr {
317 g.write(ctoslit(expr_str))
318 }
319 ast.IndexExpr {
320 g.gen_expr_to_string(expr, typ)
321 }
322 ast.PrefixExpr {
323 if expr.right is ast.CastExpr {
324 // TODO: remove this check;
325 // vlib/builtin/map_test.v (a map of &int, set to &int(0)) fails
326 // without special casing ast.CastExpr here
327 g.write(ctoslit(expr_str))
328 } else if expr.right is ast.Ident {
329 g.write(ctoslit(expr_str))
330 } else {
331 g.gen_expr_to_string(expr, typ)
332 }
333 }
334 ast.TypeNode {
335 sym := g.table.sym(g.unwrap_generic(typ))
336 g.write(ctoslit('${sym.name}'))
337 }
338 else {
339 mut should_clone := true
340 if typ == ast.string_type
341 && expr in [ast.IndexExpr, ast.CallExpr, ast.StringLiteral, ast.StringInterLiteral] {
342 should_clone = false
343 }
344 if expr is ast.CTempVar && expr.orig is ast.CallExpr {
345 should_clone = false
346 if expr.orig.or_block.kind == .propagate_option {
347 should_clone = true
348 }
349 if expr.orig.is_method && expr.orig.args.len == 0 && expr.orig.name == 'type_name' {
350 should_clone = true
351 }
352 }
353 if should_clone {
354 g.write('builtin__string_clone(')
355 }
356 g.gen_expr_to_string(expr, typ)
357 if should_clone {
358 g.write(')')
359 }
360 }
361 }
362}
363