Skip to content

Commit c16d11b

Browse files
committed
cgen: fix generic type handling bugs
Ensure generic types are properly unwrapped/resolved before generating C code: - assign.v: Unwrap generic types in expr_opt_with_cast and expr_with_opt before checking flags and generating code - cgen.v: Add safeguard in base_type() to force generic conversion when symbol name matches a generic parameter; skip registering option/result types that are still unresolved generics; properly unwrap types in expr_with_tmp_var; ensure option types from channel pop operations are registered for typedef generation - fn.v: Ensure generic flag is set before calling convert_generic_type in call_args; add fallback to receiver_type for embedded type method lookup; unwrap generic expected_type in ref_or_deref_arg
1 parent 82133f5 commit c16d11b

3 files changed

Lines changed: 103 additions & 32 deletions

File tree

‎vlib/v/gen/c/assign.v‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,9 @@ fn (mut g Gen) expr_opt_with_cast(expr ast.Expr, expr_typ ast.Type, ret_typ ast.
126126
defer {
127127
g.past_tmp_var_done(past)
128128
}
129-
styp := g.base_type(ret_typ)
130-
decl_styp := g.styp(ret_typ).replace('*', '_ptr')
129+
unwrapped_ret := g.unwrap_generic(ret_typ)
130+
styp := g.base_type(unwrapped_ret)
131+
decl_styp := g.styp(unwrapped_ret).replace('*', '_ptr')
131132
g.writeln('${decl_styp} ${past.tmp_var};')
132133
is_none := expr is ast.CastExpr && expr.expr is ast.None
133134
if is_none {
@@ -167,11 +168,14 @@ fn (mut g Gen) expr_with_opt(expr ast.Expr, expr_typ ast.Type, ret_typ ast.Type)
167168
defer {
168169
g.inside_opt_or_res = old_inside_opt_or_res
169170
}
170-
if expr_typ.has_flag(.option) && ret_typ.has_flag(.option) && !g.is_arraymap_set
171+
unwrapped_expr_typ := g.unwrap_generic(expr_typ)
172+
unwrapped_ret_typ := g.unwrap_generic(ret_typ)
173+
if unwrapped_expr_typ.has_flag(.option) && unwrapped_ret_typ.has_flag(.option)
174+
&& !g.is_arraymap_set
171175
&& expr in [ast.SelectorExpr, ast.DumpExpr, ast.Ident, ast.ComptimeSelector, ast.AsCast, ast.CallExpr, ast.MatchExpr, ast.IfExpr, ast.IndexExpr, ast.UnsafeExpr, ast.CastExpr] {
172176
if expr in [ast.Ident, ast.CastExpr] {
173-
if expr_typ.idx() != ret_typ.idx() {
174-
return g.expr_opt_with_cast(expr, expr_typ, ret_typ)
177+
if unwrapped_expr_typ.idx() != unwrapped_ret_typ.idx() {
178+
return g.expr_opt_with_cast(expr, unwrapped_expr_typ, unwrapped_ret_typ)
175179
}
176180
}
177181
g.expr(expr)
@@ -182,7 +186,8 @@ fn (mut g Gen) expr_with_opt(expr ast.Expr, expr_typ ast.Type, ret_typ ast.Type)
182186
}
183187
} else {
184188
tmp_out_var := g.new_tmp_var()
185-
g.expr_with_tmp_var(expr, expr_typ, ret_typ, tmp_out_var, true)
189+
g.expr_with_tmp_var(expr, unwrapped_expr_typ, unwrapped_ret_typ, tmp_out_var,
190+
true)
186191
return tmp_out_var
187192
}
188193
return ''

‎vlib/v/gen/c/cgen.v‎

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,7 +1281,19 @@ fn (mut g Gen) styp(t ast.Type) string {
12811281
}
12821282

12831283
fn (mut g Gen) base_type(_t ast.Type) string {
1284-
t := g.unwrap_generic(_t)
1284+
mut t := g.unwrap_generic(_t)
1285+
// Safeguard: if unwrap_generic didn't convert because the generic flag wasn't set,
1286+
// but the symbol name is a generic name, force the conversion
1287+
if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0 && g.cur_concrete_types.len > 0 {
1288+
sym := g.table.sym(t)
1289+
if sym.name in g.cur_fn.generic_names {
1290+
if utyp := g.table.convert_generic_type(t.set_flag(.generic), g.cur_fn.generic_names,
1291+
g.cur_concrete_types)
1292+
{
1293+
t = utyp
1294+
}
1295+
}
1296+
}
12851297
if styp := g.styp_cache[t] {
12861298
return styp
12871299
}
@@ -1448,13 +1460,22 @@ fn (g &Gen) result_type_text(styp string, base string) string {
14481460

14491461
fn (mut g Gen) register_option(t ast.Type) string {
14501462
styp, base := g.option_type_name(t)
1451-
g.options[base] = styp
1463+
// Only skip registration if the computed base is still an unresolved generic type name
1464+
// (base_type should have resolved generics, but check to be safe)
1465+
is_unresolved_generic := g.cur_fn != unsafe { nil } && base in g.cur_fn.generic_names
1466+
if !is_unresolved_generic {
1467+
g.options[base] = styp
1468+
}
14521469
return if !t.has_flag(.option_mut_param_t) { styp } else { '${styp}*' }
14531470
}
14541471

14551472
fn (mut g Gen) register_result(t ast.Type) string {
14561473
styp, base := g.result_type_name(t)
1457-
g.results[base] = styp
1474+
// Only skip registration if the computed base is still an unresolved generic type name
1475+
is_unresolved_generic := g.cur_fn != unsafe { nil } && base in g.cur_fn.generic_names
1476+
if !is_unresolved_generic {
1477+
g.results[base] = styp
1478+
}
14581479
return styp
14591480
}
14601481

@@ -1664,6 +1685,16 @@ fn (mut g Gen) write_chan_pop_option_fns() {
16641685
}
16651686
}
16661687
done << opt_el_type
1688+
// Ensure the option type is registered for typedef generation
1689+
// Only add if this styp is not already registered (to avoid duplicates with different bases)
1690+
option_prefix := option_name + '_'
1691+
if opt_el_type.starts_with(option_prefix) {
1692+
already_registered := opt_el_type in g.options.values()
1693+
if !already_registered {
1694+
base := opt_el_type[option_prefix.len..]
1695+
g.options[base] = opt_el_type
1696+
}
1697+
}
16671698
g.channel_definitions.writeln('
16681699
static inline ${opt_el_type} __Option_${styp}_popval(${styp} ch) {
16691700
${opt_el_type} _tmp = {0};
@@ -2379,21 +2410,23 @@ fn (mut g Gen) expr_with_tmp_var(expr ast.Expr, expr_typ ast.Type, ret_typ ast.T
23792410
} else {
23802411
g.go_before_last_stmt().trim_space()
23812412
}
2382-
mut styp := g.base_type(ret_typ)
2413+
// mut styp := g.base_type(ret_typ)
2414+
unwrapped_ret_typ := g.unwrap_generic(ret_typ)
2415+
mut styp := g.base_type(unwrapped_ret_typ)
23832416
g.empty_line = true
2384-
final_expr_sym := g.table.final_sym(expr_typ)
2417+
final_expr_sym := g.table.final_sym(g.unwrap_generic(expr_typ))
23852418
mut expected_type := ret_typ
2386-
23872419
if final_expr_sym.kind == .none {
2388-
g.write('${g.styp(ret_typ)} ${tmp_var} = ')
2389-
g.gen_option_error(ret_typ, expr)
2420+
g.write('${g.styp(unwrapped_ret_typ)} ${tmp_var} = ')
2421+
g.gen_option_error(unwrapped_ret_typ, expr)
23902422
g.writeln(';')
23912423
} else if expr is ast.Ident && expr_typ == ast.error_type {
2392-
g.writeln('${g.styp(ret_typ)} ${tmp_var} = {.state=2, .err=${expr.name}};')
2424+
g.writeln('${g.styp(unwrapped_ret_typ)} ${tmp_var} = {.state=2, .err=${expr.name}};')
23932425
} else {
23942426
mut simple_assign := false
2395-
expr_typ_is_option := expr_typ.has_flag(.option)
2396-
ret_typ_is_option := ret_typ.has_flag(.option)
2427+
unwrapped_expr_typ := g.unwrap_generic(expr_typ)
2428+
expr_typ_is_option := unwrapped_expr_typ.has_flag(.option)
2429+
ret_typ_is_option := unwrapped_ret_typ.has_flag(.option)
23972430
if ret_typ.has_flag(.generic) {
23982431
if expr is ast.SelectorExpr && g.cur_concrete_types.len == 0 {
23992432
// resolve generic struct on selectorExpr inside non-generic function
@@ -2408,16 +2441,14 @@ fn (mut g Gen) expr_with_tmp_var(expr ast.Expr, expr_typ ast.Type, ret_typ ast.T
24082441
}
24092442
}
24102443
}
2411-
unwrapped_ret_typ := g.unwrap_generic(ret_typ)
2412-
styp = g.base_type(unwrapped_ret_typ)
24132444
ret_styp := g.styp(unwrapped_ret_typ).replace('*', '_ptr')
24142445
g.writeln('${ret_styp} ${tmp_var};')
24152446
} else {
2416-
if ret_typ.has_flag(.option_mut_param_t) {
2417-
ret_styp := g.styp(ret_typ).replace('*', '')
2447+
if unwrapped_ret_typ.has_flag(.option_mut_param_t) {
2448+
ret_styp := g.styp(unwrapped_ret_typ).replace('*', '')
24182449
g.writeln('${ret_styp} ${tmp_var};')
24192450
} else {
2420-
g.writeln('${g.styp(ret_typ)} ${tmp_var};')
2451+
g.writeln('${g.styp(unwrapped_ret_typ)} ${tmp_var};')
24212452
}
24222453
}
24232454
mut expr_is_fixed_array_var := false

‎vlib/v/gen/c/fn.v‎

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2008,8 +2008,9 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
20082008
mut tmp2 := ''
20092009
cur_line := g.go_before_last_stmt()
20102010
if is_json_encode || is_json_encode_pretty {
2011-
g.gen_json_for_type(node.args[0].typ)
2012-
json_type_str = g.styp(node.args[0].typ)
2011+
unwrapped_typ := g.unwrap_generic(node.args[0].typ)
2012+
g.gen_json_for_type(unwrapped_typ)
2013+
json_type_str = g.styp(unwrapped_typ)
20132014
// `json__encode` => `json__encode_User`
20142015
encode_name := js_enc_name(json_type_str)
20152016
g.empty_line = true
@@ -2428,12 +2429,27 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
24282429
// unwrap generics fn/method arguments to concretes
24292430
if node.concrete_types.len > 0 && node.concrete_types.all(!it.has_flag(.generic)) {
24302431
if node.is_method {
2431-
if func := g.table.find_method(g.table.sym(node.left_type), node.name) {
2432+
// First try to find method on left_type, then fallback to receiver_type for embedded types
2433+
mut func := ast.Fn{}
2434+
mut found := false
2435+
if f := g.table.find_method(g.table.sym(node.left_type), node.name) {
2436+
func = f
2437+
found = true
2438+
} else if node.receiver_type != 0 && node.receiver_type != node.left_type {
2439+
// Method not found on left_type, try receiver_type (for embedded types)
2440+
if f := g.table.find_method(g.table.sym(node.receiver_type), node.name) {
2441+
func = f
2442+
found = true
2443+
}
2444+
}
2445+
if found {
24322446
if func.generic_names.len > 0 {
24332447
for i in 0 .. expected_types.len {
24342448
mut muttable := unsafe { &ast.Table(g.table) }
2435-
if utyp := muttable.convert_generic_type(node.expected_arg_types[i],
2436-
func.generic_names, node.concrete_types)
2449+
// Ensure the generic flag is set for conversion
2450+
arg_type := node.expected_arg_types[i].set_flag(.generic)
2451+
if utyp := muttable.convert_generic_type(arg_type, func.generic_names,
2452+
node.concrete_types)
24372453
{
24382454
expected_types[i] = utyp
24392455
}
@@ -2445,8 +2461,10 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
24452461
if func.generic_names.len > 0 {
24462462
for i in 0 .. expected_types.len {
24472463
mut muttable := unsafe { &ast.Table(g.table) }
2448-
if utyp := muttable.convert_generic_type(node.expected_arg_types[i],
2449-
func.generic_names, node.concrete_types)
2464+
// Ensure the generic flag is set for conversion
2465+
arg_type := node.expected_arg_types[i].set_flag(.generic)
2466+
if utyp := muttable.convert_generic_type(arg_type, func.generic_names,
2467+
node.concrete_types)
24502468
{
24512469
expected_types[i] = utyp
24522470
}
@@ -2480,15 +2498,31 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
24802498
if found && func.generic_names.len > 0 {
24812499
for i in 0 .. expected_types.len {
24822500
mut muttable := unsafe { &ast.Table(g.table) }
2483-
if utyp := muttable.convert_generic_type(node.expected_arg_types[i],
2484-
func.generic_names, left_sym.info.concrete_types)
2501+
// Ensure the generic flag is set for conversion
2502+
arg_type := node.expected_arg_types[i].set_flag(.generic)
2503+
if utyp := muttable.convert_generic_type(arg_type, func.generic_names,
2504+
left_sym.info.concrete_types)
24852505
{
24862506
expected_types[i] = utyp
24872507
}
24882508
}
24892509
}
24902510
}
24912511
}
2512+
// Final safeguard: ensure any remaining generic types from the outer function are converted
2513+
if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0 && g.cur_concrete_types.len > 0 {
2514+
for i in 0 .. expected_types.len {
2515+
sym := g.table.sym(expected_types[i])
2516+
if sym.name in g.cur_fn.generic_names {
2517+
mut muttable := unsafe { &ast.Table(g.table) }
2518+
if utyp := muttable.convert_generic_type(expected_types[i].set_flag(.generic),
2519+
g.cur_fn.generic_names, g.cur_concrete_types)
2520+
{
2521+
expected_types[i] = utyp
2522+
}
2523+
}
2524+
}
2525+
}
24922526
// only v variadic, C variadic args will be appended like normal args
24932527
is_variadic := node.language == .v && node.is_variadic && expected_types.len > 0
24942528
&& expected_types.last().has_flag(.variadic)
@@ -2758,7 +2792,8 @@ fn (mut g Gen) keep_alive_call_postgen(node ast.CallExpr, tmp_cnt_save int) {
27582792
}
27592793

27602794
@[inline]
2761-
fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang ast.Language, is_smartcast bool) {
2795+
fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type_ ast.Type, lang ast.Language, is_smartcast bool) {
2796+
expected_type := g.unwrap_generic(expected_type_)
27622797
mut arg_typ := if arg.ct_expr {
27632798
g.unwrap_generic(g.type_resolver.get_type(arg.expr))
27642799
} else {

0 commit comments

Comments
 (0)