From 7d327152ccdb86162a846408f644a9d3a337ee69 Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Sat, 27 Dec 2025 14:08:19 +0800 Subject: [PATCH] parser: fix comptime for lock shared field (fix #26143) (#26146) --- vlib/v/ast/ast.v | 1 + vlib/v/parser/lock.v | 63 ++----------------- vlib/v/parser/parser.v | 3 +- .../comptime/comptime_for_lock_field_test.v | 19 ++++++ 4 files changed, 26 insertions(+), 60 deletions(-) create mode 100644 vlib/v/tests/comptime/comptime_for_lock_field_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 3c2b798c3..490ae91dd 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -2516,6 +2516,7 @@ pub fn (e &Expr) is_lockable() bool { return match e { Ident { true } SelectorExpr { e.expr.is_lockable() } + ComptimeSelector { true } else { false } } } diff --git a/vlib/v/parser/lock.v b/vlib/v/parser/lock.v index 0893cbe7c..f4c3339a3 100644 --- a/vlib/v/parser/lock.v +++ b/vlib/v/parser/lock.v @@ -1,64 +1,7 @@ module parser -import v.token import v.ast -// parse `x` or `x.y.z` - no index, no struct literals (`{` starts lock block) -fn (mut p Parser) lockable() ast.Expr { - mut names := []string{} - mut positions := []token.Pos{} - mut pos := p.tok.pos() - for { - if p.tok.kind != .name { - p.unexpected(got: '`${p.tok.lit}`', expecting: 'field or variable name') - } - names << p.tok.lit - positions << pos - p.next() - if p.tok.kind != .dot { - break - } - p.next() - pos.extend(p.tok.pos()) - } - mut expr := ast.Expr(ast.Ident{ - language: ast.Language.v - pos: positions[0] - mod: p.mod - name: names[0] - is_mut: true - info: ast.IdentVar{} - scope: p.scope - }) - for i := 1; i < names.len; i++ { - expr = ast.SelectorExpr{ - expr: expr - field_name: names[i] - next_token: if i < names.len - 1 { token.Kind.dot } else { p.tok.kind } - is_mut: true - pos: positions[i] - scope: p.scope - } - } - return expr -} - -// like `expr_list()` but only lockables are allowed, `{` starts lock block (not struct literal) -fn (mut p Parser) lockable_list() []ast.Expr { - mut exprs := []ast.Expr{} - for { - expr := p.lockable() - if expr !is ast.Comment { - exprs << expr - if p.tok.kind != .comma { - break - } - p.next() - } - } - return exprs -} - fn (mut p Parser) lock_expr() ast.LockExpr { // TODO: Handle aliasing sync p.register_auto_import('sync') @@ -79,10 +22,12 @@ fn (mut p Parser) lock_expr() ast.LockExpr { break } if p.tok.kind == .name { - exprs := p.lockable_list() + p.inside_lock_exprs = true + exprs := p.expr_list(true) + p.inside_lock_exprs = false for e in exprs { if !e.is_lockable() { - p.error_with_pos('`${e}` cannot be locked - only `x` or `x.y` are supported', + p.error_with_pos('`${e}` cannot be locked - only `x`, `x.y` or `x.$(y)` are supported', e.pos()) } lockeds << e diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 4f4e879c4..484eabdcb 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -73,6 +73,7 @@ mut: inside_orm bool inside_chan_decl bool inside_attr_decl bool + inside_lock_exprs bool array_dim int // array dim parsing level fixed_array_dim int // fixed array dim parsing level or_is_handled bool // ignore `or` in this expression @@ -1789,7 +1790,7 @@ fn (mut p Parser) name_expr() ast.Expr { } else if !known_var && (p.peek_tok.kind == .lcbr || is_generic_struct_init) && (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital)) && !p.inside_match_case && (!p.inside_if || p.inside_select) - && (!p.inside_for || p.inside_select) { + && (!p.inside_for || p.inside_select) && !p.inside_lock_exprs { alias_array_type := p.alias_array_type() if alias_array_type != ast.void_type { return p.array_init(is_option, alias_array_type) diff --git a/vlib/v/tests/comptime/comptime_for_lock_field_test.v b/vlib/v/tests/comptime/comptime_for_lock_field_test.v new file mode 100644 index 000000000..675477bd7 --- /dev/null +++ b/vlib/v/tests/comptime/comptime_for_lock_field_test.v @@ -0,0 +1,19 @@ +struct MyS { + a int + cache shared map[u64]string +} + +fn encode[T](val T) string { + $for field in T.fields { + $if field.is_shared { + rlock val.$(field.name) { + return field.name + } + } + } + return '' +} + +fn test_comptime_for_lock_field() { + assert encode(MyS{}) == 'cache' +} -- 2.39.5