v4 / vlib / v / checker / return.v
778 lines · 750 sloc · 22.25 KB · 7d4e37c8652fee1885ef0b805cc4688555efe619
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
3module checker
4
5import v.ast
6
7// error_type_name returns a proper type name reference for error messages
8// ? => Option type
9// ! => Result type
10// others => type `name`
11fn (mut c Checker) error_type_name(exp_type ast.Type) string {
12 return if exp_type == ast.void_type.set_flag(.result) {
13 'Result type'
14 } else if exp_type == ast.void_type.set_flag(.option) {
15 'Option type'
16 } else {
17 'type `${c.table.type_to_str(exp_type)}`'
18 }
19}
20
21@[inline]
22fn (mut c Checker) error_unaliased_type_name(exp_type ast.Type) string {
23 return c.error_type_name(c.table.unaliased_type(exp_type))
24}
25
26// returns_call_like_result_expr reports whether `expr` is a `CallExpr`,
27// possibly wrapped in redundant `ParExpr`s. Used to decide whether the
28// return path is shaped like `return foo()` / `return (foo())` — the
29// shapes for which cgen can emit a `_result_*` alias clone.
30fn returns_call_like_result_expr(expr ast.Expr) bool {
31 mut e := expr
32 for e is ast.ParExpr {
33 e = e.expr
34 }
35 return e is ast.CallExpr
36}
37
38// TODO: non deferred
39fn (mut c Checker) return_stmt(mut node ast.Return) {
40 if c.table.cur_fn == unsafe { nil } {
41 return
42 }
43 prev_inside_return := c.inside_return
44 c.inside_return = true
45 defer {
46 c.inside_return = prev_inside_return
47 }
48
49 // check `defer_stmts` in return, to ensure the same behavior with `cgen`
50 old_inside_defer := c.inside_defer
51 c.inside_defer = true
52 for i := c.table.cur_fn.defer_stmts.len - 1; i >= 0; i-- {
53 c.stmts(mut c.table.cur_fn.defer_stmts[i].stmts)
54 }
55 c.inside_defer = old_inside_defer
56
57 c.expected_type = c.table.cur_fn.return_type
58 mut expected_type := c.unwrap_generic(c.expected_type)
59 if expected_type != 0 && c.table.sym(expected_type).kind == .alias {
60 unaliased_type := c.table.unaliased_type(expected_type)
61 if unaliased_type.has_option_or_result() {
62 expected_type = unaliased_type
63 }
64 }
65 expected_type_sym := c.table.sym(expected_type)
66 if expected_type_sym.info is ast.ArrayFixed {
67 c.table.find_or_register_array_fixed(expected_type_sym.info.elem_type,
68 expected_type_sym.info.size, expected_type_sym.info.size_expr, true)
69 }
70 if node.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type {
71 c.error('unexpected argument, current function does not return anything',
72 node.exprs[0].pos())
73 return
74 } else if node.exprs.len > 1 && c.table.cur_fn.return_type == ast.void_type.set_flag(.option) {
75 c.error('can only return `none` from an Option-only return function', node.exprs[0].pos())
76 return
77 } else if node.exprs.len > 1 && c.table.cur_fn.return_type == ast.void_type.set_flag(.result) {
78 c.error('functions with Result-only return types can only return an error',
79 node.exprs[0].pos())
80 return
81 } else if node.exprs.len == 0 && !(c.expected_type == ast.void_type
82 || expected_type_sym.kind == .void) {
83 stype := c.table.type_to_str(expected_type)
84 arg := if expected_type_sym.kind == .multi_return { 'arguments' } else { 'argument' }
85 c.error('expected `${stype}` ${arg}', node.pos)
86 return
87 }
88 if node.exprs.len == 0 {
89 return
90 }
91 exp_is_option := expected_type.has_flag(.option)
92 exp_is_result := expected_type.has_flag(.result)
93 mut expected_types := [expected_type]
94 if expected_type_sym.info is ast.MultiReturn {
95 expected_types = expected_type_sym.info.types.clone()
96 if c.table.cur_concrete_types.len > 0 {
97 expected_types = expected_types.map(c.unwrap_generic(it))
98 c.table.used_features.comptime_syms[c.table.find_or_register_multi_return(expected_types)] = true
99 }
100 }
101 mut got_types := []ast.Type{}
102 mut expr_idxs := []int{}
103 for i, mut expr in node.exprs {
104 mut typ := c.expr(mut expr)
105 if typ == 0 {
106 return
107 }
108 // Handle `return unsafe { none }`
109 if mut expr is ast.UnsafeExpr && expr.expr is ast.None {
110 c.error('cannot return `none` in unsafe block', expr.expr.pos)
111 }
112 if typ == ast.void_type {
113 c.error('`${expr}` used as value', node.pos)
114 return
115 }
116 // Unpack multi return types
117 sym := c.table.sym(typ)
118 if sym.kind == .multi_return {
119 if i > 0 || i != node.exprs.len - 1 {
120 c.error('cannot use multi-return with other return types', expr.pos())
121 }
122 for t in sym.mr_info().types {
123 got_types << t
124 expr_idxs << i
125 }
126 } else {
127 if mut expr is ast.Ident && expr.obj is ast.Var {
128 if expr.obj.smartcasts.len > 0 {
129 typ = c.unwrap_generic(c.visible_var_type_for_read(expr.obj))
130 }
131 if expr.obj.ct_type_var != .no_comptime {
132 typ = c.type_resolver.get_type_or_default(expr, typ)
133 }
134 if expr.obj.expr is ast.IfGuardExpr {
135 if var := expr.scope.find_var(expr.name) {
136 typ = var.typ
137 }
138 }
139 } else if mut expr is ast.SelectorExpr {
140 if expr.expr_type != 0 {
141 scope_field := expr.scope.find_struct_field(smartcast_selector_expr_str(expr),
142 expr.expr_type, expr.field_name)
143 if scope_field != unsafe { nil } && scope_field.smartcasts.len > 0 {
144 typ = c.unwrap_generic(c.exposed_smartcast_type(scope_field.orig_type,
145 scope_field.smartcasts.last(), scope_field.is_mut))
146 }
147 }
148 }
149 got_types << typ
150 expr_idxs << i
151 }
152 }
153 node.types = got_types
154 $if debug_manualfree ? {
155 cfn := c.table.cur_fn
156 if cfn.is_manualfree {
157 pnames := cfn.params.map(it.name)
158 for expr in node.exprs {
159 if expr is ast.Ident {
160 if expr.name in pnames {
161 c.note('returning a parameter in a fn marked with `@[manualfree]` can cause double freeing in the caller',
162 node.pos)
163 }
164 }
165 }
166 }
167 }
168 // allow `none` & `error` return types for function that returns option
169 option_type_idx := c.table.type_idxs['_option']
170 result_type_idx := c.table.type_idxs['_result']
171 got_types_0_idx := got_types[0].idx()
172 if exp_is_option && got_types_0_idx == ast.error_type_idx {
173 c.error('Option and Result types have been split, use `!Foo` to return errors', node.pos)
174 } else if exp_is_result && got_types_0_idx == ast.none_type_idx {
175 c.error('Option and Result types have been split, use `?` to return none', node.pos)
176 }
177 expected_fn_return_type_has_option := c.table.cur_fn.return_type.has_flag(.option)
178 expected_fn_return_type_has_result := c.table.cur_fn.return_type.has_flag(.result)
179 if exp_is_option && expected_fn_return_type_has_result {
180 if got_types_0_idx == ast.none_type_idx {
181 c.error('cannot use `none` as ${c.error_type_name(c.table.unaliased_type(c.table.cur_fn.return_type))} in return argument',
182 node.pos)
183 return
184 }
185 return
186 }
187 if exp_is_result && expected_fn_return_type_has_option {
188 c.error('expecting to return a ?Type, but you are returning ${c.error_type_name(expected_type)} instead',
189 node.pos)
190 return
191 }
192 if (exp_is_option
193 && got_types_0_idx in [ast.none_type_idx, ast.error_type_idx, option_type_idx])
194 || (exp_is_result && got_types_0_idx in [ast.error_type_idx, result_type_idx]) {
195 return
196 }
197 if expected_types.len > 0 && expected_types.len != got_types.len {
198 // `fn foo() !(int, string) { return Err{} }`
199 if (exp_is_option || exp_is_result) && node.exprs.len == 1 {
200 mut expr_ := node.exprs[0]
201 got_type := c.expr(mut expr_)
202 got_type_sym := c.table.sym(got_type)
203 if got_type_sym.kind == .struct && c.type_implements(got_type, ast.error_type, node.pos) {
204 node.exprs[0] = ast.CastExpr{
205 expr: node.exprs[0]
206 typname: 'IError'
207 typ: ast.error_type
208 expr_type: got_type
209 pos: node.pos
210 }
211 node.types[0] = ast.error_type
212 return
213 }
214 }
215 arg := if expected_types.len == 1 { 'argument' } else { 'arguments' }
216 midx := int_max(0, int_min(expected_types.len, expr_idxs.len - 1))
217 mismatch_pos := node.exprs[expr_idxs[midx]].pos()
218 c.error('expected ${expected_types.len} ${arg}, but got ${got_types.len}', mismatch_pos)
219 return
220 }
221 for i, exp_type in expected_types {
222 exprv := node.exprs[expr_idxs[i]]
223 if exprv is ast.Ident && exprv.or_expr.kind == .propagate_option {
224 if exp_type.has_flag(.option) {
225 c.warn('unwrapping option is redundant as the function returns option', node.pos)
226 } else {
227 c.error('should not unwrap option var on return, it could be none', node.pos)
228 }
229 }
230 got_type := c.unwrap_generic(got_types[i])
231 if got_type.has_flag(.option) && (!exp_type.has_flag(.option)
232 || !c.check_types(got_type.clear_flag(.option), exp_type.clear_flag(.option))) {
233 c.error('cannot use `${c.table.type_to_str(got_type)}` as ${c.error_type_name(exp_type)} in return argument',
234 exprv.pos())
235 }
236 if got_type.has_flag(.result) {
237 // Accept payload-equivalent aliases only when the expression is a
238 // call (optionally wrapped in `()`); the cgen alias clone path only
239 // handles bare calls today. For anything else, fall back to a
240 // strict identity check so cgen never emits a struct cast between
241 // distinct `_result_*` types (which C rejects).
242 payload_compat := if returns_call_like_result_expr(exprv) {
243 c.table.are_payloads_alias_compatible(got_type.clear_flag(.result),
244 exp_type.clear_flag(.result))
245 } else {
246 got_type.clear_flag(.result) == exp_type.clear_flag(.result)
247 }
248 if !exp_type.has_flag(.result) || !payload_compat {
249 c.error('cannot use `${c.table.type_to_str(got_type)}` as ${c.error_type_name(exp_type)} in return argument',
250 exprv.pos())
251 }
252 }
253 if exprv is ast.ComptimeCall && exprv.kind == .tmpl
254 && c.table.final_sym(exp_type).kind != .string {
255 c.error('cannot use `string` as type `${c.table.type_to_str(exp_type)}` in return argument',
256 exprv.pos)
257 }
258 if exprv !is ast.ComptimeCall {
259 got_type_sym := c.table.sym(got_type)
260 exp_type_sym := c.table.sym(exp_type)
261 if c.check_types(got_type, exp_type) {
262 if exp_type.is_unsigned() && got_type.is_int_literal() {
263 if exprv is ast.IntegerLiteral {
264 var := (node.exprs[expr_idxs[i]] as ast.IntegerLiteral).val
265 if var[0] == `-` {
266 c.note('cannot use a negative value as value of ${c.error_type_name(exp_type)} in return argument',
267 exprv.pos)
268 }
269 }
270 }
271 } else {
272 if c.pref.skip_unused && got_types[i].has_flag(.generic) {
273 c.table.used_features.comptime_syms[got_type] = true
274 }
275 if exp_type_sym.kind == .interface
276 || (exp_type_sym.kind == .generic_inst && exp_type_sym.info is ast.GenericInst
277 && c.table.type_symbols[exp_type_sym.info.parent_idx].kind == .interface) {
278 if c.type_implements(got_type, exp_type, node.pos) {
279 if !got_type.is_any_kind_of_pointer() && got_type_sym.kind != .interface
280 && !c.inside_unsafe {
281 c.mark_as_referenced(mut &node.exprs[expr_idxs[i]], true)
282 }
283 }
284 continue
285 }
286 exp_final_sym := c.table.final_sym(exp_type)
287 got_final_sym := c.table.final_sym(got_type)
288 if exp_final_sym.kind == .array_fixed && got_final_sym.kind == .array_fixed {
289 got_arr_sym := c.table.sym(c.cast_to_fixed_array_ret(got_type, got_final_sym))
290 if (exp_final_sym.info as ast.ArrayFixed).is_compatible(got_arr_sym.info as ast.ArrayFixed) {
291 continue
292 }
293 }
294 // `fn foo() !int { return Err{} }`
295 if expected_fn_return_type_has_result && got_type_sym.kind == .struct
296 && c.type_implements(got_type, ast.error_type, node.pos) {
297 node.exprs[expr_idxs[i]] = ast.CastExpr{
298 expr: exprv
299 typname: 'IError'
300 typ: ast.error_type
301 expr_type: got_type
302 pos: node.pos
303 }
304 node.types[expr_idxs[i]] = ast.error_type
305 continue
306 }
307 got_type_name := if got_type_sym.kind == .function {
308 '${c.table.type_to_str(got_type)}'
309 } else {
310 got_type_sym.name
311 }
312 // ignore generic lambda return in this phase
313 if c.inside_lambda && exp_type.has_flag(.generic) {
314 continue
315 }
316 if exp_final_sym.info is ast.ArrayFixed
317 && c.array_fixed_has_unresolved_size(exp_final_sym.info) {
318 continue
319 }
320 // In generic functions, scope variable types can be stale from a
321 // different instantiation pass. Skip the error if the original
322 // return type is generic — the cgen resolves types per-instantiation.
323 if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.return_type.has_flag(.generic)
324 && c.table.cur_concrete_types.len > 0 {
325 continue
326 }
327
328 c.error('cannot use `${got_type_name}` as ${c.error_type_name(exp_type)} in return argument',
329 exprv.pos())
330 }
331 }
332 if exprv is ast.Ident {
333 if exprv.obj is ast.Var && exprv.obj.smartcasts.len > 0 {
334 orig_sym := c.table.final_sym(exprv.obj.orig_type)
335 if orig_sym.kind == .interface {
336 continue
337 }
338 }
339 }
340 if got_type.is_any_kind_of_pointer() && !exp_type.is_any_kind_of_pointer()
341 && !c.table.unaliased_type(exp_type).is_any_kind_of_pointer() {
342 if exprv.is_auto_deref_var() {
343 continue
344 }
345 c.add_error_detail('use `return *pointer` instead of `return pointer`, and just `return value` instead of `return &value`')
346 c.error('fn `${c.table.cur_fn.name}` expects you to return a non reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_type)}` instead',
347 exprv.pos())
348 }
349 if exp_type.is_any_kind_of_pointer() && !got_type.is_any_kind_of_pointer()
350 && !c.table.unaliased_type(got_type).is_any_kind_of_pointer()
351 && got_type != ast.int_literal_type && !c.pref.translated && !c.file.is_translated {
352 if exprv.is_auto_deref_var() {
353 continue
354 }
355 if c.table.final_sym(exp_type).kind == .interface
356 && c.table.final_sym(got_type).kind == .interface
357 && exp_type.deref().idx() == got_type.idx() {
358 continue
359 }
360 c.error('fn `${c.table.cur_fn.name}` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_type)}` instead',
361 exprv.pos())
362 }
363 if exp_type.is_ptr() && got_type.is_ptr() {
364 r_expr := node.exprs[expr_idxs[i]]
365 if r_expr is ast.Ident {
366 mut ident_expr := r_expr
367 c.fail_if_stack_struct_action_outside_unsafe(mut ident_expr, 'returned')
368 } else if r_expr is ast.PrefixExpr && r_expr.op == .amp {
369 // &var
370 if r_expr.right is ast.Ident {
371 mut ident_expr := r_expr.right
372 c.fail_if_stack_struct_action_outside_unsafe(mut ident_expr, 'returned')
373 }
374 }
375 }
376 }
377 if exp_is_option && node.exprs.len > 0 {
378 expr0 := node.exprs[0]
379 if expr0 is ast.CallExpr {
380 if expr0.or_block.kind == .propagate_option && node.exprs.len == 1 {
381 v_name := if expr0.is_static_method {
382 expr0.name.all_before('__static__') + '.' + expr0.name.all_after('__static__')
383 } else {
384 expr0.name
385 }
386 c.error('`?` is not needed, use `return ${v_name}()`', expr0.pos)
387 }
388 }
389 }
390}
391
392fn (mut c Checker) find_unreachable_statements_after_noreturn_calls(stmts []ast.Stmt) {
393 mut prev_stmt_was_noreturn_call := false
394 for stmt in stmts {
395 if stmt is ast.ExprStmt {
396 if stmt.expr is ast.CallExpr {
397 if prev_stmt_was_noreturn_call {
398 c.warn('unreachable code after a @[noreturn] call', stmt.pos)
399 return
400 }
401 prev_stmt_was_noreturn_call = stmt.expr.is_noreturn
402 }
403 } else {
404 prev_stmt_was_noreturn_call = false
405 }
406 }
407}
408
409// Note: has_top_return/1 should be called on *already checked* stmts,
410// which do have their stmt.expr.is_noreturn set properly:
411fn (mut c Checker) has_top_return(stmts []ast.Stmt) bool {
412 for stmt in stmts {
413 match stmt {
414 ast.Return {
415 return true
416 }
417 ast.Block {
418 if c.has_top_return(stmt.stmts) {
419 return true
420 }
421 }
422 ast.ExprStmt {
423 if c.expr_never_falls_through(stmt.expr) {
424 return true
425 }
426 }
427 else {}
428 }
429 }
430 return false
431}
432
433fn (mut c Checker) expr_never_falls_through(expr ast.Expr) bool {
434 match expr {
435 ast.CallExpr {
436 // do not ignore panic() calls on non checked stmts
437 return expr.is_noreturn
438 || (expr.is_method == false && expr.name == 'panic')
439 || c.call_expr_propagates_always_error(expr)
440 }
441 ast.ComptimeCall {
442 return expr.kind == .compile_error
443 }
444 ast.LockExpr {
445 return c.has_top_return(expr.stmts)
446 }
447 ast.IfExpr {
448 return c.has_top_return_in_if_expr(expr)
449 }
450 ast.MatchExpr {
451 return c.has_top_return_in_match_expr(expr)
452 }
453 else {}
454 }
455
456 return false
457}
458
459fn (mut c Checker) call_expr_propagates_always_error(node ast.CallExpr) bool {
460 if node.should_be_skipped || node.or_block.kind != .propagate_result {
461 return false
462 }
463 mut func := ast.Fn{}
464 if node.is_method {
465 if node.left_type == 0 {
466 return false
467 }
468 left_sym := c.table.sym(c.unwrap_generic(node.left_type))
469 if left_sym.kind == .placeholder {
470 return false
471 }
472 func = c.table.find_method(left_sym, node.name) or { return false }
473 } else {
474 if node.name == '' {
475 return false
476 }
477 func = c.table.find_fn(node.name) or { return false }
478 }
479 return c.fn_always_errors(func)
480}
481
482fn (mut c Checker) fn_always_errors(func ast.Fn) bool {
483 if func.name == '' || func.source_fn == unsafe { nil } || func.no_body || func.language != .v
484 || !func.return_type.has_flag(.result) {
485 return false
486 }
487 fkey := func.fkey()
488 if fkey in c.always_error_fn_cache {
489 return c.always_error_fn_cache[fkey]
490 }
491 if fkey in c.always_error_fn_in_progress {
492 return false
493 }
494 c.always_error_fn_in_progress[fkey] = true
495 defer {
496 c.always_error_fn_in_progress.delete(fkey)
497 }
498 fn_decl := unsafe { &ast.FnDecl(func.source_fn) }
499 result := c.stmts_always_error(fn_decl.stmts)
500 c.always_error_fn_cache[fkey] = result
501 return result
502}
503
504fn (mut c Checker) stmts_always_error(stmts []ast.Stmt) bool {
505 for stmt in stmts {
506 if c.stmt_always_errors(stmt) {
507 return true
508 }
509 }
510 return false
511}
512
513fn (mut c Checker) stmt_always_errors(stmt ast.Stmt) bool {
514 match stmt {
515 ast.Return {
516 return c.return_stmt_always_errors(stmt)
517 }
518 ast.Block {
519 return c.stmts_always_error(stmt.stmts)
520 }
521 ast.ExprStmt {
522 return c.expr_always_errors(stmt.expr)
523 }
524 ast.ForStmt {
525 return stmt.is_inf && stmt.stmts.len == 0
526 }
527 else {}
528 }
529
530 return false
531}
532
533fn (mut c Checker) expr_always_errors(expr ast.Expr) bool {
534 match expr {
535 ast.CallExpr {
536 return expr.is_noreturn || c.call_expr_propagates_always_error(expr)
537 }
538 ast.ComptimeCall {
539 return expr.kind == .compile_error
540 }
541 ast.IfExpr {
542 return c.if_expr_always_errors(expr)
543 }
544 ast.MatchExpr {
545 return c.match_expr_always_errors(expr)
546 }
547 ast.LockExpr {
548 return c.stmts_always_error(expr.stmts)
549 }
550 else {}
551 }
552
553 return false
554}
555
556fn (mut c Checker) return_stmt_always_errors(node ast.Return) bool {
557 if node.types.len == 1 && c.table.unaliased_type(node.types[0]).idx() == ast.error_type_idx {
558 return true
559 }
560 return false
561}
562
563fn (mut c Checker) has_top_return_in_if_expr(if_expr ast.IfExpr) bool {
564 if if_expr.branches.len < 2 || !if_expr.has_else {
565 return false
566 }
567 for branch in if_expr.branches {
568 if !c.has_top_return(branch.stmts) {
569 return false
570 }
571 }
572 return true
573}
574
575fn (mut c Checker) if_expr_always_errors(if_expr ast.IfExpr) bool {
576 if if_expr.branches.len < 2 || !if_expr.has_else {
577 return false
578 }
579 for branch in if_expr.branches {
580 if !c.stmts_always_error(branch.stmts) {
581 return false
582 }
583 }
584 return true
585}
586
587fn (mut c Checker) has_top_return_in_match_expr(match_expr ast.MatchExpr) bool {
588 if match_expr.branches.len == 0 {
589 return false
590 }
591 if match_expr.is_comptime {
592 mut has_else := false
593 for branch in match_expr.branches {
594 if branch.is_else {
595 has_else = true
596 break
597 }
598 for expr in branch.exprs {
599 if expr is ast.Ident && expr.name == '\$else' {
600 has_else = true
601 break
602 }
603 }
604 if has_else {
605 break
606 }
607 }
608 if has_else {
609 for branch in match_expr.branches {
610 if !c.has_top_return(branch.stmts) {
611 return false
612 }
613 }
614 return true
615 }
616 return false
617 }
618 for branch in match_expr.branches {
619 if !c.has_top_return(branch.stmts) {
620 return false
621 }
622 }
623 return true
624}
625
626fn (mut c Checker) match_expr_always_errors(match_expr ast.MatchExpr) bool {
627 if match_expr.branches.len == 0 {
628 return false
629 }
630 if match_expr.is_comptime {
631 mut has_else := false
632 for branch in match_expr.branches {
633 if branch.is_else {
634 has_else = true
635 break
636 }
637 for expr in branch.exprs {
638 if expr is ast.Ident && expr.name == '\$else' {
639 has_else = true
640 break
641 }
642 }
643 if has_else {
644 break
645 }
646 }
647 if !has_else {
648 return false
649 }
650 }
651 for branch in match_expr.branches {
652 if !c.stmts_always_error(branch.stmts) {
653 return false
654 }
655 }
656 return true
657}
658
659fn (mut c Checker) check_noreturn_fn_decl(mut node ast.FnDecl) {
660 if !node.is_noreturn {
661 return
662 }
663 if node.no_body {
664 return
665 }
666 if node.return_type != ast.void_type {
667 c.error('[noreturn] functions cannot have return types', node.pos)
668 }
669 if uses_return_stmt(node.stmts) {
670 c.error('[noreturn] functions cannot use return statements', node.pos)
671 }
672 mut pos := node.pos
673 mut is_valid_end_of_noreturn_fn := false
674 if node.stmts.len != 0 {
675 last_stmt := node.stmts.last()
676 match last_stmt {
677 ast.ExprStmt {
678 if last_stmt.expr is ast.CallExpr {
679 if last_stmt.expr.should_be_skipped {
680 c.error('@[noreturn] functions cannot end with a skippable `@[if ..]` call',
681 last_stmt.pos)
682 return
683 }
684 if last_stmt.expr.is_noreturn {
685 is_valid_end_of_noreturn_fn = true
686 }
687 }
688 }
689 ast.ForStmt {
690 if last_stmt.is_inf && last_stmt.stmts.len == 0 {
691 is_valid_end_of_noreturn_fn = true
692 }
693 }
694 else {}
695 }
696
697 if !is_valid_end_of_noreturn_fn {
698 pos = last_stmt.pos
699 }
700 }
701 if !is_valid_end_of_noreturn_fn {
702 c.error('@[noreturn] functions should end with a call to another @[noreturn] function, or with an infinite `for {}` loop',
703 pos)
704 }
705}
706
707fn uses_return_stmt(stmts []ast.Stmt) bool {
708 if stmts.len == 0 {
709 return false
710 }
711 for stmt in stmts {
712 match stmt {
713 ast.Return {
714 return true
715 }
716 ast.Block {
717 if uses_return_stmt(stmt.stmts) {
718 return true
719 }
720 }
721 ast.ExprStmt {
722 match stmt.expr {
723 ast.CallExpr {
724 if uses_return_stmt(stmt.expr.or_block.stmts) {
725 return true
726 }
727 }
728 ast.MatchExpr {
729 for b in stmt.expr.branches {
730 if uses_return_stmt(b.stmts) {
731 return true
732 }
733 }
734 }
735 ast.SelectExpr {
736 for b in stmt.expr.branches {
737 if uses_return_stmt(b.stmts) {
738 return true
739 }
740 }
741 }
742 ast.IfExpr {
743 for b in stmt.expr.branches {
744 if uses_return_stmt(b.stmts) {
745 return true
746 }
747 }
748 }
749 else {}
750 }
751 }
752 ast.ForStmt {
753 if uses_return_stmt(stmt.stmts) {
754 return true
755 }
756 }
757 ast.ForCStmt {
758 if uses_return_stmt(stmt.stmts) {
759 return true
760 }
761 }
762 ast.ForInStmt {
763 if uses_return_stmt(stmt.stmts) {
764 return true
765 }
766 }
767 else {}
768 }
769 }
770 return false
771}
772
773fn is_noreturn_callexpr(expr ast.Expr) bool {
774 if expr is ast.CallExpr {
775 return expr.is_noreturn
776 }
777 return false
778}
779