Skip to content

Commit b215d11

Browse files
authored
cgen: cast member variables to correct types in comptime $for (fix #25771) (#25773)
1 parent aae3ba8 commit b215d11

File tree

4 files changed

+54
-18
lines changed

4 files changed

+54
-18
lines changed

vlib/v/gen/c/cgen.v

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5293,6 +5293,27 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
52935293
}
52945294
}
52955295

5296+
fn (mut g Gen) is_comptime_for_var(node ast.Ident) bool {
5297+
if !g.comptime.inside_comptime_for || g.comptime.comptime_for_field_var == '' {
5298+
return false
5299+
}
5300+
if node.obj is ast.Var {
5301+
obj_typ := node.obj.typ
5302+
field_typ := g.comptime.comptime_for_field_type
5303+
return (obj_typ.has_flag(.option) && field_typ.has_flag(.option))
5304+
|| (obj_typ.clear_flag(.option).idx() == field_typ.clear_flag(.option).idx()
5305+
&& obj_typ.has_flag(.option))
5306+
}
5307+
return false
5308+
}
5309+
5310+
fn (mut g Gen) get_comptime_for_var_type(node ast.Ident, default_typ ast.Type) ast.Type {
5311+
if g.is_comptime_for_var(node) {
5312+
return g.comptime.comptime_for_field_type
5313+
}
5314+
return default_typ
5315+
}
5316+
52965317
fn (mut g Gen) ident(node ast.Ident) {
52975318
prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once
52985319
g.prevent_sum_type_unwrapping_once = false
@@ -5391,7 +5412,8 @@ fn (mut g Gen) ident(node ast.Ident) {
53915412
g.write(name)
53925413
}
53935414
} else {
5394-
g.unwrap_option_type(node.info.typ, name, is_auto_heap)
5415+
unwrap_typ := g.get_comptime_for_var_type(node, node.info.typ)
5416+
g.unwrap_option_type(unwrap_typ, name, is_auto_heap)
53955417
}
53965418
if node.or_expr.kind != .absent && !(g.inside_opt_or_res && g.inside_assign
53975419
&& !g.is_assign_lhs) {
@@ -5439,7 +5461,9 @@ fn (mut g Gen) ident(node ast.Ident) {
54395461
g.write('(*(')
54405462
}
54415463
is_option = is_option || node.obj.orig_type.has_flag(.option)
5442-
if node.obj.smartcasts.len > 0 {
5464+
// Skip smartcasts for comptime_for variables to avoid using stale types
5465+
skip_smartcasts := g.is_comptime_for_var(node)
5466+
if node.obj.smartcasts.len > 0 && !skip_smartcasts {
54435467
obj_sym := g.table.final_sym(g.unwrap_generic(node.obj.typ))
54445468
if !prevent_sum_type_unwrapping_once {
54455469
nested_unwrap := node.obj.smartcasts.len > 1
@@ -5473,7 +5497,8 @@ fn (mut g Gen) ident(node ast.Ident) {
54735497
&& g.table.type_kind(node.obj.typ) == .any) {
54745498
g.write('*')
54755499
} else if is_option {
5476-
g.write('*(${g.base_type(node.obj.typ)}*)')
5500+
unwrap_typ := g.get_comptime_for_var_type(node, node.obj.typ)
5501+
g.write('*(${g.base_type(unwrap_typ)}*)')
54775502
}
54785503
}
54795504
for i, typ in node.obj.smartcasts {

vlib/v/gen/c/str_intp.v

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int, fmts []u8) {
220220
g.write2('${dot}_object', ')')
221221
} else if fmt == `s` || typ.has_flag(.variadic) {
222222
mut exp_typ := typ
223-
if expr is ast.Ident {
223+
is_comptime_for_var := expr is ast.Ident && g.is_comptime_for_var(expr)
224+
if !is_comptime_for_var && expr is ast.Ident {
224225
if g.comptime.get_ct_type_var(expr) == .smartcast {
225226
exp_typ = g.type_resolver.get_type(expr)
226227
} else if expr.obj is ast.Var {
@@ -236,7 +237,17 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int, fmts []u8) {
236237
}
237238
}
238239
}
239-
g.gen_expr_to_string(expr, exp_typ)
240+
if exp_typ.has_flag(.option) && expr is ast.Ident && g.is_comptime_for_var(expr) {
241+
str_fn_name := g.get_str_fn(exp_typ.clear_flag(.option))
242+
g.write('${str_fn_name}(*(${g.base_type(exp_typ)}*)(')
243+
old_inside_opt_or_res := g.inside_opt_or_res
244+
g.inside_opt_or_res = true
245+
g.expr(expr)
246+
g.inside_opt_or_res = old_inside_opt_or_res
247+
g.write('.data))')
248+
} else {
249+
g.gen_expr_to_string(expr, exp_typ)
250+
}
240251
} else if typ.is_number() || typ.is_pointer() || fmt == `d` {
241252
if typ.is_signed() && fmt in [`x`, `X`, `o`] {
242253
// convert to unsigned first befors C's integer propagation strikes
@@ -283,26 +294,26 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
283294
mut node_ := unsafe { node }
284295
mut fmts := node_.fmts.clone()
285296
for i, mut expr in node_.exprs {
286-
mut type_to_use := node_.expr_types[i]
297+
mut field_typ := if mut expr is ast.Ident && g.is_comptime_for_var(expr) {
298+
g.comptime.comptime_for_field_type
299+
} else {
300+
node_.expr_types[i]
301+
}
287302
if g.comptime.inside_comptime_for && mut expr is ast.SelectorExpr {
288303
if expr.expr is ast.TypeOf && expr.field_name == 'name' {
289-
// This is typeof(var).name
290304
typeof_expr := expr.expr as ast.TypeOf
291305
if typeof_expr.expr is ast.Ident {
292306
ident_name := (typeof_expr.expr as ast.Ident).name
293-
// Check if this ident might be from a multi-return in the current scope
294-
// For now, force re-evaluation by looking it up in the comptime context
295307
if obj := typeof_expr.expr.scope.find(ident_name) {
296308
if obj is ast.Var {
297-
// Use the var's actual type, which might be correct in codegen context
298-
type_to_use = obj.typ
309+
field_typ = obj.typ
299310
}
300311
}
301312
}
302313
}
303314
}
304-
if g.comptime.is_comptime(expr) {
305-
ctyp := g.type_resolver.get_type_or_default(expr, node_.expr_types[i])
315+
if g.comptime.is_comptime(expr) || (g.comptime.inside_comptime_for && expr is ast.Ident) {
316+
ctyp := g.type_resolver.get_type_or_default(expr, field_typ)
306317
if ctyp != ast.void_type {
307318
node_.expr_types[i] = ctyp
308319
if node_.fmts[i] == `_` {
@@ -316,7 +327,7 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
316327
}
317328
}
318329
} else {
319-
node_.expr_types[i] = type_to_use
330+
node_.expr_types[i] = field_typ
320331
}
321332
}
322333
g.write2('builtin__str_intp(', node.vals.len.str())

vlib/v/tests/comptime/comptime_for_in_options_struct_test.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ fn unwrap_not_none_field_types[T](t T) []string {
1010
v := t.$(f.name)
1111
$if f is $option {
1212
if v != none {
13-
arr << typeof(v).name
13+
arr << '${typeof(v).name}:${f.name}=`${v}`'
1414
}
1515
}
1616
}
@@ -23,5 +23,5 @@ fn test_main() {
2323
b: 1
2424
c: 2.3
2525
})
26-
assert arr.join(' ') == 'string int f64'
26+
assert arr.join(' ') == 'string:a=`x` int:b=`1` f64:c=`2.3`'
2727
}

vlib/v/tests/comptime/comptime_var_assignment_test.v

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ fn test_main() {
2626
out := encode_struct(FixedStruct1{1, 'foo', 4, 'bar'})
2727
assert out[0] == '1'
2828
assert out[1] == 'foo'
29-
assert out[2] == 'Option(4)'
30-
assert out[3] == "Option('bar')"
29+
assert out[2] == '4'
30+
assert out[3] == 'bar'
3131
}

0 commit comments

Comments
 (0)