Skip to content

Commit 915ff40

Browse files
authored
checker: check invalid comptime field name assignment (fix #24415) (#24421)
1 parent 3bf8f42 commit 915ff40

8 files changed

+54
-4
lines changed

vlib/v/checker/assign.v

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -876,13 +876,20 @@ or use an explicit `unsafe{ a[..] }`, if you do not want a copy of the slice.',
876876
node.pos)
877877
}
878878
} else {
879-
// allow `t.$(field.name) = 0` where `t.$(field.name)` is a enum
880879
if c.comptime.comptime_for_field_var != '' && left is ast.ComptimeSelector {
881-
field_sym := c.table.sym(c.unwrap_generic(c.comptime.comptime_for_field_type))
880+
field_type := c.unwrap_generic(c.comptime.comptime_for_field_type)
881+
field_sym := c.table.sym(field_type)
882882

883+
// allow `t.$(field.name) = 0` where `t.$(field.name)` is a enum
883884
if field_sym.kind == .enum && !right_type.is_int() {
884885
c.error('enums can only be assigned `int` values', right.pos())
885886
}
887+
// disallow invalid `t.$(field.name)` type assignment
888+
if !c.check_types(field_type, right_type) && !(c.inside_x_matches_type
889+
|| field_sym.kind == .enum) {
890+
c.error('cannot assign to `${left}`: ${c.expected_msg(right_type,
891+
field_type)}', right.pos())
892+
}
886893
} else {
887894
if right_type_unwrapped != ast.void_type {
888895
if !var_option || (var_option && right_type_unwrapped != ast.none_type) {

vlib/v/checker/checker.v

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ pub mut:
8989
inside_fn_arg bool // `a`, `b` in `a.f(b)`
9090
inside_ct_attr bool // true inside `[if expr]`
9191
inside_x_is_type bool // true inside the Type expression of `if x is Type {`
92+
inside_x_matches_type bool // true inside the match branch of `match x.type { Type {} }`
9293
anon_struct_should_be_mut bool // true when `mut var := struct { ... }` is used
9394
inside_generic_struct_init bool
9495
inside_integer_literal_cast bool // true inside `int(123)`
@@ -192,6 +193,7 @@ fn (mut c Checker) reset_checker_state_at_start_of_new_file() {
192193
c.inside_fn_arg = false
193194
c.inside_ct_attr = false
194195
c.inside_x_is_type = false
196+
c.inside_x_matches_type = false
195197
c.inside_integer_literal_cast = false
196198
c.skip_flags = false
197199
c.fn_level = 0

vlib/v/checker/comptime.v

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
274274
has_different_types := fields.len > 1
275275
&& !fields.all(c.check_basic(it.typ, fields[0].typ))
276276
for field in fields {
277+
prev_inside_x_matches_type := c.inside_x_matches_type
277278
c.push_new_comptime_info()
278279
c.comptime.inside_comptime_for = true
279280
c.table.used_features.comptime_for = true
@@ -301,6 +302,7 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
301302
}
302303
}
303304
c.pop_comptime_info()
305+
c.inside_x_matches_type = prev_inside_x_matches_type
304306
}
305307
} else if node.typ != ast.void_type && c.table.generic_type_names(node.typ).len == 0
306308
&& sym.kind != .placeholder {

vlib/v/checker/match.v

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
414414
}
415415
continue
416416
}
417+
is_type_node := expr is ast.TypeNode
417418
match mut expr {
418419
ast.TypeNode {
419420
key = c.table.type_to_str(expr.typ)
@@ -438,6 +439,9 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
438439
c.error('match case `${key}` is handled more than once', branch.pos)
439440
}
440441
c.expected_type = node.cond_type
442+
if is_type_node {
443+
c.inside_x_matches_type = true
444+
}
441445
expr_type := c.expr(mut expr)
442446
if expr_type.idx() == 0 {
443447
// parser failed, stop checking
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
vlib/v/checker/tests/assign_enum_at_comptime.vv:13:21: error: enums can only be assigned `int` values
2-
11 |
2+
11 |
33
12 | $for field in TestStruct.fields {
44
13 | t.$(field.name) = '1'
55
| ~~~
66
14 | }
7-
15 | }
7+
15 | }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
vlib/v/checker/tests/comptime_field_name_assign_incorrect_type_err.vv:11:25: error: cannot assign to `t.$(field.name)`: expected `int`, not `string`
2+
9 | mut t := T{}
3+
10 | $for field in T.fields {
4+
11 | t.$(field.name) = data[field.name]
5+
| ~~~~~~~~~~~~
6+
12 | }
7+
13 | return t
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module main
2+
3+
struct Record {
4+
mut:
5+
f1 int
6+
}
7+
8+
fn map_data[T](data map[string]string) T {
9+
mut t := T{}
10+
$for field in T.fields {
11+
t.$(field.name) = data[field.name]
12+
}
13+
return t
14+
}
15+
16+
fn main() {
17+
mut data := map[string]string{}
18+
data['f1'] = '123'
19+
t := map_data[Record](data)
20+
println('bug1 : ${t}')
21+
}

vlib/v/checker/tests/comptime_selector_assign.out

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,10 @@ vlib/v/checker/tests/comptime_selector_assign.vv:18:24: error: mismatched types:
55
| ^
66
19 | }
77
20 | }
8+
vlib/v/checker/tests/comptime_selector_assign.vv:18:24: error: cannot assign to `typ.$(field.name)`: expected `string`, not `int literal`
9+
16 | typ.$(field.name) = 2
10+
17 | }
11+
18 | typ.$(field.name) = 3
12+
| ^
13+
19 | }
14+
20 | }

0 commit comments

Comments
 (0)