Skip to content

Commit 20aefee

Browse files
authored
checker,cgen,type_resolver: prevent stale type cast on comptime $for, handle in dumpexpr (fix #25781) (#25784)
1 parent 8f91815 commit 20aefee

File tree

8 files changed

+149
-24
lines changed

8 files changed

+149
-24
lines changed

vlib/v/checker/checker.v

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3179,6 +3179,18 @@ pub fn (mut c Checker) expr(mut node ast.Expr) ast.Type {
31793179
} else if (node.expr as ast.Ident).name in c.type_resolver.type_map {
31803180
node.expr_type = c.type_resolver.get_ct_type_or_default((node.expr as ast.Ident).name,
31813181
node.expr_type)
3182+
} else if node.expr.obj is ast.Var {
3183+
var_obj := node.expr.obj as ast.Var
3184+
if var_obj.smartcasts.len > 0 {
3185+
node.expr_type = c.unwrap_generic(var_obj.smartcasts.last())
3186+
}
3187+
}
3188+
} else if mut node.expr is ast.Ident {
3189+
if node.expr.obj is ast.Var {
3190+
var_obj := node.expr.obj as ast.Var
3191+
if var_obj.smartcasts.len > 0 {
3192+
node.expr_type = c.unwrap_generic(var_obj.smartcasts.last())
3193+
}
31823194
}
31833195
}
31843196
c.check_expr_option_or_result_call(node.expr, node.expr_type)

vlib/v/checker/if.v

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,10 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
114114
c.cur_ct_id++
115115
branch.id = c.cur_ct_id
116116
}
117-
idx_str := comptime_branch_context_str + '|id=${branch.id}|'
117+
mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
118+
if c.comptime.inside_comptime_for && c.comptime.comptime_for_field_var != '' {
119+
idx_str += '|field_type=${c.comptime.comptime_for_field_type}|'
120+
}
118121
c.comptime.inside_comptime_if = true
119122
mut sb := strings.new_builder(256)
120123
comptime_if_result, comptime_if_multi_pass_branch = c.comptime_if_cond(mut branch.cond, mut
@@ -186,7 +189,10 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
186189
c.cur_ct_id++
187190
branch.id = c.cur_ct_id
188191
}
189-
idx_str := comptime_branch_context_str + '|id=${branch.id}|'
192+
mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
193+
if c.comptime.inside_comptime_for && c.comptime.comptime_for_field_var != '' {
194+
idx_str += '|field_type=${c.comptime.comptime_for_field_type}|'
195+
}
190196
c.table.comptime_is_true[idx_str] = ast.ComptTimeCondResult{
191197
val: comptime_if_result
192198
c_str: ''
@@ -517,13 +523,35 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co
517523
|| (node.left is ast.SelectorExpr && node.left.is_mut) {
518524
c.fail_if_immutable(mut node.left)
519525
}
520-
if node.left is ast.Ident && c.comptime.get_ct_type_var(node.left) == .smartcast {
521-
node.left_type = c.type_resolver.get_type(node.left)
522-
c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut
523-
scope, true, true)
526+
if c.comptime.inside_comptime_for && c.comptime.comptime_for_field_var != ''
527+
&& node.left is ast.Ident {
528+
if mut node.left is ast.Ident {
529+
if mut node.left.obj is ast.Var {
530+
if node.left.obj.ct_type_var == .field_var {
531+
scope.register(ast.Var{
532+
name: node.left.name
533+
typ: node.left_type
534+
pos: node.left.pos
535+
is_used: true
536+
is_mut: node.left.is_mut
537+
is_inherited: node.left.obj.is_inherited
538+
is_unwrapped: true
539+
orig_type: node.left_type
540+
ct_type_var: .field_var
541+
ct_type_unwrapped: true
542+
})
543+
}
544+
}
545+
}
524546
} else {
525-
c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut
526-
scope, false, true)
547+
if node.left is ast.Ident && c.comptime.get_ct_type_var(node.left) == .smartcast {
548+
node.left_type = c.type_resolver.get_type(node.left)
549+
c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut
550+
scope, true, true)
551+
} else {
552+
c.smartcast(mut node.left, node.left_type, node.left_type.clear_flag(.option), mut
553+
scope, false, true)
554+
}
527555
}
528556
} else if node.op == .key_is {
529557
if node.left is ast.Ident && node.left.ct_expr {
@@ -593,9 +621,15 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co
593621
node.left.pos)
594622
}
595623
}
596-
if left_final_sym.kind in [.interface, .sum_type] {
624+
is_option_unwrap := node.left_type.has_flag(.option)
625+
&& !right_type.has_flag(.option)
626+
skip_smartcast := c.comptime.inside_comptime_for
627+
&& c.comptime.comptime_for_field_var != '' && node.left is ast.Ident
628+
&& (node.left as ast.Ident).name == c.comptime.comptime_for_field_var
629+
if !skip_smartcast
630+
&& (left_final_sym.kind in [.interface, .sum_type] || is_option_unwrap) {
597631
c.smartcast(mut node.left, node.left_type, right_type, mut
598-
scope, is_comptime, false)
632+
scope, is_comptime, is_option_unwrap)
599633
}
600634
}
601635
}

vlib/v/checker/match.v

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,10 @@ fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type {
137137
c.cur_ct_id++
138138
branch.id = c.cur_ct_id
139139
}
140-
idx_str := comptime_branch_context_str + '|id=${branch.id}|'
140+
mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
141+
if c.comptime.inside_comptime_for && c.comptime.comptime_for_field_var != '' {
142+
idx_str += '|field_type=${c.comptime.comptime_for_field_type}|'
143+
}
141144
mut c_str := ''
142145
if !branch.is_else {
143146
if c.inside_x_matches_type {

vlib/v/gen/c/cgen.v

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5353,7 +5353,12 @@ fn (mut g Gen) ident(node ast.Ident) {
53535353
if !g.is_assign_lhs
53545354
&& node.obj.ct_type_var !in [.smartcast, .generic_param, .no_comptime, .aggregate] {
53555355
comptime_type := g.type_resolver.get_type(node)
5356-
if comptime_type.has_flag(.option) {
5356+
orig_has_option := node.obj.typ.has_flag(.option)
5357+
if orig_has_option && !comptime_type.has_flag(.option) {
5358+
styp := g.base_type(comptime_type)
5359+
ptr := if is_auto_heap { '->' } else { '.' }
5360+
g.write('(*(${styp}*)${name}${ptr}data)')
5361+
} else if comptime_type.has_flag(.option) {
53575362
if (g.inside_opt_or_res || g.left_is_opt) && node.or_expr.kind == .absent {
53585363
if !g.is_assign_lhs && is_auto_heap {
53595364
g.write('(*${name})')
@@ -5362,11 +5367,8 @@ fn (mut g Gen) ident(node ast.Ident) {
53625367
}
53635368
} else {
53645369
styp := g.base_type(comptime_type)
5365-
if is_auto_heap {
5366-
g.write('(*(${styp}*)${name}->data)')
5367-
} else {
5368-
g.write('(*(${styp}*)${name}.data)')
5369-
}
5370+
ptr := if is_auto_heap { '->' } else { '.' }
5371+
g.write('(*(${styp}*)${name}${ptr}data)')
53705372
}
53715373
} else {
53725374
if is_auto_heap {
@@ -5872,7 +5874,10 @@ fn (mut g Gen) gen_hash_stmts(mut sb strings.Builder, node &ast.HashStmtNode, se
58725874
mut comptime_branch_context_str := g.gen_branch_context_string()
58735875
mut is_true := ast.ComptTimeCondResult{}
58745876
for i, branch in node.branches {
5875-
idx_str := comptime_branch_context_str + '|id=${branch.id}|'
5877+
mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
5878+
if g.comptime.inside_comptime_for && g.comptime.comptime_for_field_var != '' {
5879+
idx_str += '|field_type=${g.comptime.comptime_for_field_type}|'
5880+
}
58765881
if comptime_is_true := g.table.comptime_is_true[idx_str] {
58775882
// `g.table.comptime_is_true` are the branch condition results set by `checker`
58785883
is_true = comptime_is_true

vlib/v/gen/c/comptime.v

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,10 @@ fn (mut g Gen) comptime_if(node ast.IfExpr) {
423423
// The first part represents the current context of the branch statement, `comptime_branch_context_str`, formatted like `T=int,X=string,method.name=json`
424424
// The second part is the branch's id.
425425
// This format must match what is in `checker`.
426-
idx_str := comptime_branch_context_str + '|id=${branch.id}|'
426+
mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
427+
if g.comptime.inside_comptime_for && g.comptime.comptime_for_field_var != '' {
428+
idx_str += '|field_type=${g.comptime.comptime_for_field_type}|'
429+
}
427430
if comptime_is_true := g.table.comptime_is_true[idx_str] {
428431
// `g.table.comptime_is_true` are the branch condition results set by `checker`
429432
is_true = comptime_is_true
@@ -1013,7 +1016,10 @@ fn (mut g Gen) comptime_match(node ast.MatchExpr) {
10131016
// The first part represents the current context of the branch statement, `comptime_branch_context_str`, formatted like `T=int,X=string,method.name=json`
10141017
// The second part is the branch's id.
10151018
// This format must match what is in `checker`.
1016-
idx_str := comptime_branch_context_str + '|id=${branch.id}|'
1019+
mut idx_str := comptime_branch_context_str + '|id=${branch.id}|'
1020+
if g.comptime.inside_comptime_for && g.comptime.comptime_for_field_var != '' {
1021+
idx_str += '|field_type=${g.comptime.comptime_for_field_type}|'
1022+
}
10171023
if comptime_is_true := g.table.comptime_is_true[idx_str] {
10181024
// `g.table.comptime_is_true` are the branch condition results set by `checker`
10191025
is_true = comptime_is_true

vlib/v/gen/c/dumpexpr.v

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,21 @@ fn (mut g Gen) dump_expr(node ast.DumpExpr) {
4242
}
4343
}
4444
} else if node.expr is ast.Ident && node.expr.ct_expr {
45-
expr_type = g.type_resolver.get_type(node.expr)
45+
for {
46+
if node.expr.obj is ast.Var {
47+
if node.expr.obj.ct_type_var == .field_var && g.comptime.inside_comptime_for
48+
&& g.comptime.comptime_for_field_var != '' {
49+
expr_type = if node.expr.obj.ct_type_unwrapped || node.expr.obj.is_unwrapped {
50+
g.comptime.comptime_for_field_type.clear_flag(.option)
51+
} else {
52+
g.comptime.comptime_for_field_type
53+
}
54+
break
55+
}
56+
}
57+
expr_type = g.type_resolver.get_type(node.expr)
58+
break
59+
}
4660
name = g.styp(g.unwrap_generic(expr_type.clear_flags(.shared_f, .result))).replace('*',
4761
'')
4862
} else if node.expr is ast.SelectorExpr && node.expr.expr is ast.Ident
@@ -83,7 +97,25 @@ fn (mut g Gen) dump_expr(node ast.DumpExpr) {
8397
if expr_type.has_flag(.option_mut_param_t) {
8498
g.write('*')
8599
}
86-
g.expr(node.expr)
100+
for {
101+
if node.expr is ast.Ident {
102+
if node.expr.obj is ast.Var {
103+
if node.expr.obj.ct_type_var == .field_var && g.comptime.inside_comptime_for
104+
&& (node.expr.obj.ct_type_unwrapped || node.expr.obj.is_unwrapped) {
105+
field_type := g.comptime.comptime_for_field_type
106+
if field_type.has_flag(.option) {
107+
styp := g.base_type(field_type.clear_flag(.option))
108+
is_auto_heap := node.expr.is_auto_heap()
109+
ptr := if is_auto_heap { '->' } else { '.' }
110+
g.write('(*(${styp}*)${c_name(node.expr.name)}${ptr}data)')
111+
break
112+
}
113+
}
114+
}
115+
}
116+
g.expr(node.expr)
117+
break
118+
}
87119
g.inside_opt_or_res = old_inside_opt_or_res
88120
}
89121
g.write(')')

vlib/v/tests/comptime/comptime_for_in_options_struct_test.v

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,40 @@ fn unwrap_not_none_field_types[T](t T) []string {
1111
$if f is $option {
1212
if v != none {
1313
arr << '${typeof(v).name}:${f.name}=`${v}`'
14+
15+
w := v // assign
16+
17+
$if w is string {
18+
t_string(w) // fn call with string value
19+
}
20+
21+
t_generic(w) // fn call with generic value
1422
}
1523
}
1624
}
1725
return arr
1826
}
1927

28+
fn t_string(s string) {
29+
assert s == 'x'
30+
}
31+
32+
fn t_generic[T](t T) {
33+
$if t is int {
34+
assert t == 1
35+
return
36+
}
37+
$if t is f64 {
38+
assert t == 2.3
39+
return
40+
}
41+
$if t is string {
42+
t_string(t)
43+
return
44+
}
45+
assert false
46+
}
47+
2048
fn test_main() {
2149
arr := unwrap_not_none_field_types(Options{
2250
a: 'x'

vlib/v/type_resolver/type_resolver.v

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,13 @@ pub fn (mut t TypeResolver) get_type(node ast.Expr) ast.Type {
207207
}
208208
.field_var {
209209
// field var from $for loop
210-
if node.obj.ct_type_unwrapped {
211-
t.info.comptime_for_field_type.clear_flag(.option)
210+
unwrapped_field_type := t.info.comptime_for_field_type.clear_flag(.option)
211+
if t.info.comptime_for_field_type.has_flag(.option)
212+
&& node.obj.typ != ast.void_type && !node.obj.typ.has_flag(.option)
213+
&& node.obj.typ == unwrapped_field_type {
214+
node.obj.typ
215+
} else if node.obj.ct_type_unwrapped {
216+
unwrapped_field_type
212217
} else {
213218
t.info.comptime_for_field_type
214219
}

0 commit comments

Comments
 (0)