From 42f890983c383187ca5bf73b5eda57e2cf194f58 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 25 Mar 2026 16:42:18 +0300 Subject: [PATCH] cgen: fix showing the line where interface is used as generic type (fixes #23004) --- .../run/json_encode_generic_interface.run.out | 6 ++++ .../run/json_encode_generic_interface.vv | 11 ++++++ vlib/v/gen/c/cgen.v | 8 +++++ vlib/v/gen/c/fn.v | 4 +-- vlib/v/gen/c/json.v | 35 ++++++++++++++++--- 5 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 vlib/v/checker/tests/run/json_encode_generic_interface.run.out create mode 100644 vlib/v/checker/tests/run/json_encode_generic_interface.vv diff --git a/vlib/v/checker/tests/run/json_encode_generic_interface.run.out b/vlib/v/checker/tests/run/json_encode_generic_interface.run.out new file mode 100644 index 000000000..2b1e350ef --- /dev/null +++ b/vlib/v/checker/tests/run/json_encode_generic_interface.run.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/run/json_encode_generic_interface.vv:10:22: cgen error: json: Interface is not struct + 8 | + 9 | fn main() { + 10 | println(json.encode(Result[Interface]{})) + | ~~~~~~~~~~~~~~~~~~~ + 11 | } diff --git a/vlib/v/checker/tests/run/json_encode_generic_interface.vv b/vlib/v/checker/tests/run/json_encode_generic_interface.vv new file mode 100644 index 000000000..f5914db8c --- /dev/null +++ b/vlib/v/checker/tests/run/json_encode_generic_interface.vv @@ -0,0 +1,11 @@ +import json + +interface Interface {} + +struct Result[T] { + data []T +} + +fn main() { + println(json.encode(Result[Interface]{})) +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 4cd7ef9d1..589f7c141 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -44,6 +44,7 @@ mut: out strings.Builder extern_out strings.Builder // extern declarations for -parallel-cc // line_nr int +<<<<<<< HEAD cheaders strings.Builder preincludes strings.Builder // allows includes to go before `definitions` postincludes strings.Builder // allows includes to go after all the rest of the code generation @@ -213,6 +214,8 @@ mut: sumtype_definitions map[u32]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated trace_fn_definitions []string json_types []ast.Type // to avoid json gen duplicates + json_types_pos map[ast.Type]token.Pos + json_gen_pos token.Pos pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name hotcode_fn_names []string hotcode_fpaths []string @@ -501,6 +504,11 @@ pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) GenO global_g.array_get_types << g.array_get_types global_g.pcs << g.pcs global_g.json_types << g.json_types + for k, v in g.json_types_pos { + if k !in global_g.json_types_pos || global_g.json_types_pos[k] == token.Pos{} { + global_g.json_types_pos[k] = v + } + } global_g.hotcode_fn_names << g.hotcode_fn_names global_g.hotcode_fpaths << g.hotcode_fpaths global_g.test_function_names << g.test_function_names diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 3a5d9a0de..6d02aa58d 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -4487,7 +4487,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { if resolved_json_arg_type != 0 { unwrapped_typ = g.unwrap_generic(g.recheck_concrete_type(resolved_json_arg_type)) } - g.gen_json_for_type(unwrapped_typ) + g.gen_json_for_type_with_pos(unwrapped_typ, node.args[0].expr.pos()) json_type_str = g.styp(unwrapped_typ) // `json__encode` => `json__encode_User` encode_name := js_enc_name(json_type_str) @@ -4511,7 +4511,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { // `json.decode(User, s)` => json.decode_User(s) typ := c_name(g.styp(ast_type.typ)) fn_name := c_fn_name(name) + '_' + typ - g.gen_json_for_type(ast_type.typ) + g.gen_json_for_type_with_pos(ast_type.typ, node.args[0].expr.pos()) g.empty_line = true g.writeln('// json.decode') g.write('cJSON* ${json_obj} = json__json_parse(') diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v index acbcb0759..a653be784 100644 --- a/vlib/v/gen/c/json.v +++ b/vlib/v/gen/c/json.v @@ -4,6 +4,7 @@ module c import v.ast +import v.token import v.util import strings @@ -29,9 +30,32 @@ fn (mut g Gen) gen_json_for_type(typ ast.Type) { if is_js_prim(sym.name) && !utyp.has_flag(.option) && !typ.is_ptr() { return } + if g.json_gen_pos != token.Pos{} + && (utyp !in g.json_types_pos || g.json_types_pos[utyp] == token.Pos{}) { + g.json_types_pos[utyp] = g.json_gen_pos + } g.json_types << utyp } +fn (mut g Gen) gen_json_for_type_with_pos(typ ast.Type, pos token.Pos) { + saved_json_gen_pos := g.json_gen_pos + g.json_gen_pos = pos + g.gen_json_for_type(typ) + g.json_gen_pos = saved_json_gen_pos +} + +fn (mut g Gen) json_error(typ ast.Type, s string) { + utyp := g.unwrap_generic(typ) + mut pos := g.json_gen_pos + if utyp in g.json_types_pos { + pos = g.json_types_pos[utyp] + } + if pos != token.Pos{} { + g.error(s, pos) + } + verror(s) +} + fn (mut g Gen) gen_jsons() { mut done := []ast.Type{} for i := 0; i < g.json_types.len; i++ { @@ -40,6 +64,8 @@ fn (mut g Gen) gen_jsons() { continue } done << utyp + saved_json_gen_pos := g.json_gen_pos + g.json_gen_pos = g.json_types_pos[utyp] mut dec := strings.new_builder(100) mut enc := strings.new_builder(100) sym := g.table.sym(utyp) @@ -194,7 +220,7 @@ ${enc_fn_dec} { } else if psym.kind == .enum { g.gen_enum_enc_dec(utyp, psym, mut enc, mut dec) } else if psym.kind == .sum_type { - verror('json: ${sym.name} aliased sumtypes does not work at the moment') + g.json_error(utyp, 'json: ${sym.name} aliased sumtypes does not work at the moment') } else if psym.kind == .map { m := psym.info as ast.Map g.gen_json_for_type(m.key_type) @@ -204,13 +230,13 @@ ${enc_fn_dec} { } else if utyp.has_flag(.option) { g.gen_option_enc_dec(utyp, mut enc, mut dec) } else { - verror('json: ${sym.name} is not struct') + g.json_error(utyp, 'json: ${sym.name} is not struct') } } else if sym.kind == .sum_type { enc.writeln('\to = cJSON_CreateObject();') // Sumtypes. Range through variants of sumtype if sym.info !is ast.SumType { - verror('json: ${sym.name} is not a sumtype') + g.json_error(utyp, 'json: ${sym.name} is not a sumtype') } g.gen_sumtype_enc_dec(utyp, sym, mut enc, mut dec, ret_styp) } else if sym.kind == .enum { @@ -222,7 +248,7 @@ ${enc_fn_dec} { enc.writeln('\to = cJSON_CreateObject();') // Structs. Range through fields if sym.info !is ast.Struct { - verror('json: ${sym.name} is not struct') + g.json_error(utyp, 'json: ${sym.name} is not struct') } g.gen_struct_enc_dec(utyp, sym.info, ret_styp, mut enc, mut dec, '') } @@ -233,6 +259,7 @@ ${enc_fn_dec} { enc.writeln('\treturn o;\n}') g.gowrappers.writeln(dec.str()) g.gowrappers.writeln(enc.str()) + g.json_gen_pos = saved_json_gen_pos } } -- 2.39.5