From f108d8312414079cb7e08ef9cd18c348ac30820a Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Mon, 22 Dec 2025 01:34:10 +0800 Subject: [PATCH] checker: make sure `$for` eval body statements at least once to set types and avoid markused issues later (fix #26058) (#26063) --- vlib/v/checker/comptime.v | 50 +++++++++++++------ .../comptime_for_empty_loop_eval_stmts_test.v | 44 ++++++++++++++++ 2 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 vlib/v/tests/comptime/comptime_for_empty_loop_eval_stmts_test.v diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index e9c0bac40..69fe866bc 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -287,6 +287,12 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { } has_different_types := fields.len > 1 && !fields.all(c.check_basic(it.typ, fields[0].typ)) + if fields.len == 0 { + // force eval `node.stmts` to set their types + fields << ast.StructField{ + typ: ast.error_type + } + } for field in fields { c.push_new_comptime_info() prev_inside_x_matches_type := c.inside_x_matches_type @@ -302,15 +308,17 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { c.comptime.has_different_types = has_different_types c.stmts(mut node.stmts) - unwrapped_expr_type := c.unwrap_generic(field.typ) - tsym := c.table.sym(unwrapped_expr_type) - c.markused_comptimefor(mut node, unwrapped_expr_type) - if tsym.kind == .array_fixed { - info := tsym.info as ast.ArrayFixed - if !info.is_fn_ret { - // for dumping fixed array we must register the fixed array struct to return from function - c.table.find_or_register_array_fixed(info.elem_type, info.size, - info.size_expr, true) + if field.typ != ast.no_type { + unwrapped_expr_type := c.unwrap_generic(field.typ) + tsym := c.table.sym(unwrapped_expr_type) + c.markused_comptimefor(mut node, unwrapped_expr_type) + if tsym.kind == .array_fixed { + info := tsym.info as ast.ArrayFixed + if !info.is_fn_ret { + // for dumping fixed array we must register the fixed array struct to return from function + c.table.find_or_register_array_fixed(info.elem_type, info.size, + info.size_expr, true) + } } } c.inside_x_matches_type = prev_inside_x_matches_type @@ -344,7 +352,11 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { return } } else if node.kind == .methods { - methods := sym.get_methods() + mut methods := sym.get_methods() + if methods.len == 0 { + // force eval `node.stmts` to set their types + methods << ast.Fn{} + } for method in methods { c.push_new_comptime_info() c.comptime.inside_comptime_for = true @@ -352,8 +364,10 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { c.comptime.comptime_for_method_var = node.val_var c.comptime.comptime_for_method_ret_type = method.return_type c.type_resolver.update_ct_type('${node.val_var}.return_type', method.return_type) - for j, arg in method.params[1..] { - c.type_resolver.update_ct_type('${node.val_var}.args[${j}].typ', arg.typ.idx()) + if method.params.len > 0 { + for j, arg in method.params[1..] { + c.type_resolver.update_ct_type('${node.val_var}.args[${j}].typ', arg.typ.idx()) + } } c.stmts(mut node.stmts) c.pop_comptime_info() @@ -366,7 +380,11 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { } func := if sym.info is ast.FnType { &sym.info.func } else { c.comptime.comptime_for_method } - params := if func.is_method { func.params[1..] } else { func.params } + mut params := if func.is_method { func.params[1..] } else { func.params } + if params.len == 0 { + // force eval `node.stmts` to set their types + params << ast.Param{} + } // example: fn (mut d MyStruct) add(x int, y int) string // `d` is params[0], `x` is params[1], `y` is params[2] // so we at least has one param (`d`) for method @@ -379,7 +397,11 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { c.pop_comptime_info() } } else if node.kind == .attributes { - attrs := c.table.get_attrs(sym) + mut attrs := c.table.get_attrs(sym) + if attrs.len == 0 { + // force eval `node.stmts` to set their types + attrs << ast.Attr{} + } for attr in attrs { c.push_new_comptime_info() c.comptime.inside_comptime_for = true diff --git a/vlib/v/tests/comptime/comptime_for_empty_loop_eval_stmts_test.v b/vlib/v/tests/comptime/comptime_for_empty_loop_eval_stmts_test.v new file mode 100644 index 000000000..68ba9e870 --- /dev/null +++ b/vlib/v/tests/comptime/comptime_for_empty_loop_eval_stmts_test.v @@ -0,0 +1,44 @@ +module main + +fn encode_struct[T](val T) { + $for _ in T.fields { + a, b := 'fdsf'.split_once(',') or { '1', '2' } + assert a == '1' + assert b == '2' + } + $for _ in T.attributes { + a, b := 'fdsf'.split_once(',') or { '1', '2' } + assert a == '1' + assert b == '2' + } + $for _ in T.methods { + a, b := 'fdsf'.split_once(',') or { '1', '2' } + assert a == '1' + assert b == '2' + } + $for m in T.methods { + $for _ in m.params { + a, b := 'fdsf'.split_once(',') or { '1', '2' } + assert a == '1' + assert b == '2' + } + } +} + +struct EmptyStruct { +} + +struct EmptyParam { +} + +// method without params +fn (e EmptyParam) method() { +} + +fn test_comptime_for_empty_loop_eval_stmts() { + x := EmptyStruct{} + encode_struct(x) + + y := EmptyParam{} + encode_struct(y) +} -- 2.39.5