From 2e23f2a18b02ebfc805f2dc038f75acbdc72af3a Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Thu, 21 Aug 2025 15:27:30 +0800 Subject: [PATCH] checker: ensure the defer behavior matches that of cgen (fix #25148) (#25146) --- vlib/v/checker/fn.v | 10 +++++++++ vlib/v/checker/return.v | 9 ++++++++ vlib/v/checker/tests/comptime_defer_err.out | 21 +++++++++++++++++++ vlib/v/checker/tests/comptime_defer_err.vv | 23 +++++++++++++++++++++ vlib/v/checker/tests/defer_in_for.out | 7 +++++++ 5 files changed, 70 insertions(+) create mode 100644 vlib/v/checker/tests/comptime_defer_err.out create mode 100644 vlib/v/checker/tests/comptime_defer_err.vv diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index ef32676db..73f27910a 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -558,6 +558,16 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { c.error('missing return at end of function `${node.name}`', node.pos) } } + if !node.has_return { + // for `node.has_return`, checking in `return.v` `return_stmt` + old_inside_defer := c.inside_defer + c.inside_defer = true + for i := c.table.cur_fn.defer_stmts.len - 1; i >= 0; i-- { + c.stmts(mut c.table.cur_fn.defer_stmts[i].stmts) + } + c.inside_defer = old_inside_defer + } + node.source_file = c.file if node.name in c.table.fns { diff --git a/vlib/v/checker/return.v b/vlib/v/checker/return.v index 883fa31da..966fddb47 100644 --- a/vlib/v/checker/return.v +++ b/vlib/v/checker/return.v @@ -33,6 +33,15 @@ fn (mut c Checker) return_stmt(mut node ast.Return) { defer { c.inside_return = prev_inside_return } + + // check `defer_stmts` in return, to ensure the same behavior with `cgen` + old_inside_defer := c.inside_defer + c.inside_defer = true + for i := c.table.cur_fn.defer_stmts.len - 1; i >= 0; i-- { + c.stmts(mut c.table.cur_fn.defer_stmts[i].stmts) + } + c.inside_defer = old_inside_defer + c.expected_type = c.table.cur_fn.return_type mut expected_type := c.unwrap_generic(c.expected_type) if expected_type != 0 && c.table.sym(expected_type).kind == .alias { diff --git a/vlib/v/checker/tests/comptime_defer_err.out b/vlib/v/checker/tests/comptime_defer_err.out new file mode 100644 index 000000000..46f3a0469 --- /dev/null +++ b/vlib/v/checker/tests/comptime_defer_err.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/comptime_defer_err.vv:17:14: error: compile time field access can only be used when iterating over `T.fields` + 15 | $if field.typ is string { + 16 | defer { + 17 | if tst.$(field.name) == 'tst-s' { + | ~~~~~ + 18 | println('found tst-s') + 19 | } +vlib/v/checker/tests/comptime_defer_err.vv:17:14: error: unknown `$for` variable `field` + 15 | $if field.typ is string { + 16 | defer { + 17 | if tst.$(field.name) == 'tst-s' { + | ~~~~~ + 18 | println('found tst-s') + 19 | } +vlib/v/checker/tests/comptime_defer_err.vv:17:12: error: non-bool type `void` used as if condition + 15 | $if field.typ is string { + 16 | defer { + 17 | if tst.$(field.name) == 'tst-s' { + | ~~~~~~~~~~~~~~~~~~~~~~~~ + 18 | println('found tst-s') + 19 | } diff --git a/vlib/v/checker/tests/comptime_defer_err.vv b/vlib/v/checker/tests/comptime_defer_err.vv new file mode 100644 index 000000000..162b4402b --- /dev/null +++ b/vlib/v/checker/tests/comptime_defer_err.vv @@ -0,0 +1,23 @@ +module main + +struct Struct { + s string + i int +} + +fn main() { + tst := Struct{ + s: 'tst-s' + i: 42 + } + + $for field in Struct.fields { + $if field.typ is string { + defer { + if tst.$(field.name) == 'tst-s' { + println('found tst-s') + } + } + } + } +} diff --git a/vlib/v/checker/tests/defer_in_for.out b/vlib/v/checker/tests/defer_in_for.out index 56c7b9190..96f13146a 100644 --- a/vlib/v/checker/tests/defer_in_for.out +++ b/vlib/v/checker/tests/defer_in_for.out @@ -5,3 +5,10 @@ vlib/v/checker/tests/defer_in_for.vv:4:4: error: `break` is not allowed in defer | ~~~~~ 5 | } 6 | } +vlib/v/checker/tests/defer_in_for.vv:4:4: error: break statement not within a loop + 2 | for true { + 3 | defer { + 4 | break + | ~~~~~ + 5 | } + 6 | } -- 2.39.5