From b2a0f6814232681059a42ceb616ff9a518382172 Mon Sep 17 00:00:00 2001 From: CreeperFace <165158232+dy-tea@users.noreply.github.com> Date: Sat, 15 Nov 2025 12:40:17 +0000 Subject: [PATCH] cgen: fix comptime for types not being recognized with maps correctly (fix #25742) (#25743) --- vlib/v/gen/c/assign.v | 53 +++++++++++++++---- vlib/v/gen/c/str_intp.v | 25 +++++++++ .../comptime_map_fields_decode_test.v | 24 +++++++++ 3 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 vlib/v/tests/comptime/comptime_map_fields_decode_test.v diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index a83a1958f..252685ca5 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -1132,17 +1132,51 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { fn (mut g Gen) gen_multi_return_assign(node &ast.AssignStmt, return_type ast.Type, return_sym ast.TypeSymbol) { // multi return // TODO: Handle in if_expr - mr_var_name := 'mr_${node.pos.pos}' - mut is_option := return_type.has_flag(.option) - mut mr_styp := g.styp(return_type.clear_flag(.result)) + mut ret_type := return_type + mut ret_sym := return_sym + mut suffix := '' + if g.comptime.inside_comptime_for && node.right[0] is ast.CallExpr { + call_expr := node.right[0] as ast.CallExpr + if call_expr.concrete_types.len > 0 && return_sym.info is ast.MultiReturn + && g.comptime.comptime_for_field_var != '' { + field_type := g.comptime.comptime_for_field_type + field_sym := g.table.sym(field_type) + if field_sym.info is ast.Map { + map_info := field_sym.info as ast.Map + ret_type = g.table.find_or_register_multi_return([map_info.key_type, map_info.value_type]) + ret_sym = *g.table.sym(ret_type) + } + } + if g.comptime.comptime_for_field_var != '' { + suffix = '_${g.comptime.comptime_for_field_value.name}' + } + } + mr_var_name := 'mr_${node.pos.pos}${suffix}' + mut is_option := ret_type.has_flag(.option) + mut mr_styp := g.styp(ret_type.clear_flag(.result)) if node.right[0] is ast.CallExpr && node.right[0].or_block.kind != .absent { is_option = false - mr_styp = g.styp(return_type.clear_option_and_result()) + mr_styp = g.styp(ret_type.clear_option_and_result()) } g.write('${mr_styp} ${mr_var_name} = ') g.expr(node.right[0]) g.writeln(';') - mr_types := (return_sym.info as ast.MultiReturn).types + mr_types := (ret_sym.info as ast.MultiReturn).types + mut recompute_types := false + if g.comptime.inside_comptime_for && node.right[0] is ast.CallExpr { + call_expr := node.right[0] as ast.CallExpr + if call_expr.concrete_types.len > 0 && g.comptime.comptime_for_field_var != '' + && return_sym.info is ast.MultiReturn { + recompute_types = true + for i, mut lx in &node.left { + if mut lx is ast.Ident && lx.kind != .blank_ident { + if mut lx.obj is ast.Var { + lx.obj.typ = mr_types[i] + } + } + } + } + } for i, lx in node.left { mut cur_indexexpr := -1 mut is_auto_heap := false @@ -1161,7 +1195,8 @@ fn (mut g Gen) gen_multi_return_assign(node &ast.AssignStmt, return_type ast.Typ if lx is ast.IndexExpr && g.cur_indexexpr.len > 0 { cur_indexexpr = g.cur_indexexpr.index(lx.pos.pos) } - styp := if ident.name in g.defer_vars { '' } else { g.styp(node.left_types[i]) } + left_type := if recompute_types { mr_types[i] } else { node.left_types[i] } + styp := if ident.name in g.defer_vars { '' } else { g.styp(left_type) } if node.op == .decl_assign { g.write('${styp} ') } @@ -1170,14 +1205,14 @@ fn (mut g Gen) gen_multi_return_assign(node &ast.AssignStmt, return_type ast.Typ } noscan := if is_auto_heap { g.check_noscan(return_type) } else { '' } mut aligned := 0 - sym := g.table.final_sym(node.left_types[i]) + sym := g.table.final_sym(left_type) if sym.info is ast.Struct { if attr := sym.info.attrs.find_first('aligned') { aligned = if attr.arg == '' { 0 } else { attr.arg.int() } } } - if node.left_types[i].has_flag(.option) { - base_typ := g.base_type(node.left_types[i]) + if left_type.has_flag(.option) { + base_typ := g.base_type(left_type) tmp_var := if is_auto_heap { if aligned != 0 { 'HEAP_align(${styp}, ${mr_var_name}.arg${i}, ${aligned})' diff --git a/vlib/v/gen/c/str_intp.v b/vlib/v/gen/c/str_intp.v index 4dfaa70a4..369da7eac 100644 --- a/vlib/v/gen/c/str_intp.v +++ b/vlib/v/gen/c/str_intp.v @@ -191,6 +191,11 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int, fmts []u8) { fmt := fmts[i] typ := g.unwrap_generic(node.expr_types[i]) typ_sym := g.table.sym(typ) + if g.comptime.inside_comptime_for && expr is ast.SelectorExpr && expr.field_name == 'name' + && expr.expr is ast.TypeOf { + g.expr(expr) + return + } if typ == ast.string_type && g.comptime.comptime_for_method == unsafe { nil } { if g.inside_vweb_tmpl { g.write('${g.vweb_filter_fn_name}(') @@ -278,6 +283,24 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { mut node_ := unsafe { node } mut fmts := node_.fmts.clone() for i, mut expr in node_.exprs { + mut type_to_use := node_.expr_types[i] + if g.comptime.inside_comptime_for && mut expr is ast.SelectorExpr { + if expr.expr is ast.TypeOf && expr.field_name == 'name' { + // This is typeof(var).name + typeof_expr := expr.expr as ast.TypeOf + if typeof_expr.expr is ast.Ident { + ident_name := (typeof_expr.expr as ast.Ident).name + // Check if this ident might be from a multi-return in the current scope + // For now, force re-evaluation by looking it up in the comptime context + if obj := typeof_expr.expr.scope.find(ident_name) { + if obj is ast.Var { + // Use the var's actual type, which might be correct in codegen context + type_to_use = obj.typ + } + } + } + } + } if g.comptime.is_comptime(expr) { ctyp := g.type_resolver.get_type_or_default(expr, node_.expr_types[i]) if ctyp != ast.void_type { @@ -292,6 +315,8 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { fmts[i] = g.get_default_fmt(ctyp, typ) } } + } else { + node_.expr_types[i] = type_to_use } } g.write2('builtin__str_intp(', node.vals.len.str()) diff --git a/vlib/v/tests/comptime/comptime_map_fields_decode_test.v b/vlib/v/tests/comptime/comptime_map_fields_decode_test.v new file mode 100644 index 000000000..2b21347ea --- /dev/null +++ b/vlib/v/tests/comptime/comptime_map_fields_decode_test.v @@ -0,0 +1,24 @@ +struct Maps { + a map[string]int + b map[string]bool + c map[string]f64 +} + +fn decode_struct[T](t T) []string { + mut decoded := []string{} + $for f in T.fields { + $if f is $map { + k, v := map_key_value(t.$(f.name)) + decoded << '${typeof(k).name} ${typeof(v).name}' + } + } + return decoded +} + +fn map_key_value[K, V](m map[K]V) (K, V) { + return K{}, V{} +} + +fn test_main() { + assert decode_struct(Maps{}) == ['string int', 'string bool', 'string f64'] +} -- 2.39.5