Skip to content

Commit f108d83

Browse files
authored
checker: make sure $for eval body statements at least once to set types and avoid markused issues later (fix #26058) (#26063)
1 parent 703eca8 commit f108d83

File tree

2 files changed

+80
-14
lines changed

2 files changed

+80
-14
lines changed

vlib/v/checker/comptime.v

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
287287
}
288288
has_different_types := fields.len > 1
289289
&& !fields.all(c.check_basic(it.typ, fields[0].typ))
290+
if fields.len == 0 {
291+
// force eval `node.stmts` to set their types
292+
fields << ast.StructField{
293+
typ: ast.error_type
294+
}
295+
}
290296
for field in fields {
291297
c.push_new_comptime_info()
292298
prev_inside_x_matches_type := c.inside_x_matches_type
@@ -302,15 +308,17 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
302308
c.comptime.has_different_types = has_different_types
303309
c.stmts(mut node.stmts)
304310

305-
unwrapped_expr_type := c.unwrap_generic(field.typ)
306-
tsym := c.table.sym(unwrapped_expr_type)
307-
c.markused_comptimefor(mut node, unwrapped_expr_type)
308-
if tsym.kind == .array_fixed {
309-
info := tsym.info as ast.ArrayFixed
310-
if !info.is_fn_ret {
311-
// for dumping fixed array we must register the fixed array struct to return from function
312-
c.table.find_or_register_array_fixed(info.elem_type, info.size,
313-
info.size_expr, true)
311+
if field.typ != ast.no_type {
312+
unwrapped_expr_type := c.unwrap_generic(field.typ)
313+
tsym := c.table.sym(unwrapped_expr_type)
314+
c.markused_comptimefor(mut node, unwrapped_expr_type)
315+
if tsym.kind == .array_fixed {
316+
info := tsym.info as ast.ArrayFixed
317+
if !info.is_fn_ret {
318+
// for dumping fixed array we must register the fixed array struct to return from function
319+
c.table.find_or_register_array_fixed(info.elem_type, info.size,
320+
info.size_expr, true)
321+
}
314322
}
315323
}
316324
c.inside_x_matches_type = prev_inside_x_matches_type
@@ -344,16 +352,22 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
344352
return
345353
}
346354
} else if node.kind == .methods {
347-
methods := sym.get_methods()
355+
mut methods := sym.get_methods()
356+
if methods.len == 0 {
357+
// force eval `node.stmts` to set their types
358+
methods << ast.Fn{}
359+
}
348360
for method in methods {
349361
c.push_new_comptime_info()
350362
c.comptime.inside_comptime_for = true
351363
c.comptime.comptime_for_method = unsafe { &method }
352364
c.comptime.comptime_for_method_var = node.val_var
353365
c.comptime.comptime_for_method_ret_type = method.return_type
354366
c.type_resolver.update_ct_type('${node.val_var}.return_type', method.return_type)
355-
for j, arg in method.params[1..] {
356-
c.type_resolver.update_ct_type('${node.val_var}.args[${j}].typ', arg.typ.idx())
367+
if method.params.len > 0 {
368+
for j, arg in method.params[1..] {
369+
c.type_resolver.update_ct_type('${node.val_var}.args[${j}].typ', arg.typ.idx())
370+
}
357371
}
358372
c.stmts(mut node.stmts)
359373
c.pop_comptime_info()
@@ -366,7 +380,11 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
366380
}
367381

368382
func := if sym.info is ast.FnType { &sym.info.func } else { c.comptime.comptime_for_method }
369-
params := if func.is_method { func.params[1..] } else { func.params }
383+
mut params := if func.is_method { func.params[1..] } else { func.params }
384+
if params.len == 0 {
385+
// force eval `node.stmts` to set their types
386+
params << ast.Param{}
387+
}
370388
// example: fn (mut d MyStruct) add(x int, y int) string
371389
// `d` is params[0], `x` is params[1], `y` is params[2]
372390
// so we at least has one param (`d`) for method
@@ -379,7 +397,11 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
379397
c.pop_comptime_info()
380398
}
381399
} else if node.kind == .attributes {
382-
attrs := c.table.get_attrs(sym)
400+
mut attrs := c.table.get_attrs(sym)
401+
if attrs.len == 0 {
402+
// force eval `node.stmts` to set their types
403+
attrs << ast.Attr{}
404+
}
383405
for attr in attrs {
384406
c.push_new_comptime_info()
385407
c.comptime.inside_comptime_for = true
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module main
2+
3+
fn encode_struct[T](val T) {
4+
$for _ in T.fields {
5+
a, b := 'fdsf'.split_once(',') or { '1', '2' }
6+
assert a == '1'
7+
assert b == '2'
8+
}
9+
$for _ in T.attributes {
10+
a, b := 'fdsf'.split_once(',') or { '1', '2' }
11+
assert a == '1'
12+
assert b == '2'
13+
}
14+
$for _ in T.methods {
15+
a, b := 'fdsf'.split_once(',') or { '1', '2' }
16+
assert a == '1'
17+
assert b == '2'
18+
}
19+
$for m in T.methods {
20+
$for _ in m.params {
21+
a, b := 'fdsf'.split_once(',') or { '1', '2' }
22+
assert a == '1'
23+
assert b == '2'
24+
}
25+
}
26+
}
27+
28+
struct EmptyStruct {
29+
}
30+
31+
struct EmptyParam {
32+
}
33+
34+
// method without params
35+
fn (e EmptyParam) method() {
36+
}
37+
38+
fn test_comptime_for_empty_loop_eval_stmts() {
39+
x := EmptyStruct{}
40+
encode_struct(x)
41+
42+
y := EmptyParam{}
43+
encode_struct(y)
44+
}

0 commit comments

Comments
 (0)