Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parser, checker: fix var scope in lambda(fix #19860) #19871

Merged
merged 1 commit into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub mut:
inside_unsafe bool // true inside `unsafe {}` blocks
inside_const bool // true inside `const ( ... )` blocks
inside_anon_fn bool // true inside `fn() { ... }()`
inside_lambda bool // true inside `|...| ...`
inside_ref_lit bool // true inside `a := &something`
inside_defer bool // true inside `defer {}` blocks
inside_fn_arg bool // `a`, `b` in `a.f(b)`
Expand Down Expand Up @@ -2799,6 +2800,10 @@ pub fn (mut c Checker) expr(mut node ast.Expr) ast.Type {
return c.int_lit(mut node)
}
ast.LambdaExpr {
c.inside_lambda = true
defer {
c.inside_lambda = false
}
return c.lambda_expr(mut node, c.expected_type)
}
ast.LockExpr {
Expand Down Expand Up @@ -3732,8 +3737,13 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
found_var := c.fn_scope.find_var(node.name)

if found_var != none {
c.error('`${node.name}` must be added to the capture list for the closure to be used inside',
node.pos)
if c.inside_lambda {
// Lambdas don't support capturing variables yet, so that's the only hint.
c.error('undefined variable `${node.name}`', node.pos)
} else {
c.error('`${node.name}` must be added to the capture list for the closure to be used inside',
node.pos)
}
return ast.void_type
}
}
Expand Down
33 changes: 33 additions & 0 deletions vlib/v/checker/tests/lambda_undefined_variables_err.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
vlib/v/checker/tests/lambda_undefined_variables_err.vv:7:2: warning: unused variable: `s2`
5 | f(|x| s1)
6 |
7 | s2 := 'abc'
| ~~
8 | f(|x| s2)
9 | }
vlib/v/checker/tests/lambda_undefined_variables_err.vv:5:8: error: undefined ident: `s1`
3 |
4 | fn main() {
5 | f(|x| s1)
| ~~
6 |
7 | s2 := 'abc'
vlib/v/checker/tests/lambda_undefined_variables_err.vv:5:4: error: `s1` used as value
3 |
4 | fn main() {
5 | f(|x| s1)
| ^
6 |
7 | s2 := 'abc'
vlib/v/checker/tests/lambda_undefined_variables_err.vv:8:8: error: undefined variable `s2`
6 |
7 | s2 := 'abc'
8 | f(|x| s2)
| ~~
9 | }
vlib/v/checker/tests/lambda_undefined_variables_err.vv:8:4: error: `s2` used as value
6 |
7 | s2 := 'abc'
8 | f(|x| s2)
| ^
9 | }
9 changes: 9 additions & 0 deletions vlib/v/checker/tests/lambda_undefined_variables_err.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fn f(g fn (int) string) {
}

fn main() {
f(|x| s1)

s2 := 'abc'
f(|x| s2)
}
1 change: 1 addition & 0 deletions vlib/v/parser/expr.v
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,7 @@ fn (mut p Parser) lambda_expr() ?ast.LambdaExpr {
defer {
p.close_scope()
}
p.scope.detached_from_parent = true

mut pos := p.tok.pos()
mut params := []ast.Ident{}
Expand Down
Loading