Skip to content

Commit 162a6e9

Browse files
authored
cgen: fix fn arg for struct nested fields (fix #26346) (#26349)
1 parent b291d49 commit 162a6e9

File tree

4 files changed

+120
-7
lines changed

4 files changed

+120
-7
lines changed

vlib/v/ast/ast.v

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2483,6 +2483,31 @@ pub fn (expr Expr) is_lvalue() bool {
24832483
}
24842484
}
24852485

2486+
// Helper function to check if an expression ultimately refers to a function argument
2487+
// This handles cases like `p.s`, `p.inner.data`, etc. where `p` is a function argument
2488+
pub fn (expr Expr) is_fn_arg() bool {
2489+
return match expr {
2490+
Ident {
2491+
if expr.obj is Var {
2492+
expr.obj.is_arg
2493+
} else {
2494+
false
2495+
}
2496+
}
2497+
SelectorExpr {
2498+
// Recursively check if the base expression is a function argument
2499+
expr.expr.is_fn_arg()
2500+
}
2501+
IndexExpr {
2502+
// Array indexing like `arr[0]` - check if `arr` is a function argument
2503+
expr.left.is_fn_arg()
2504+
}
2505+
else {
2506+
false
2507+
}
2508+
}
2509+
}
2510+
24862511
pub fn (expr Expr) is_expr() bool {
24872512
return match expr {
24882513
IfExpr, LockExpr, MatchExpr, SelectExpr { expr.is_expr }

vlib/v/gen/c/cgen.v

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2986,18 +2986,19 @@ fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Ty
29862986
&& g.table.sym(got).kind in [.i8, .i16, .i32, .int, .i64, .isize, .u8, .u16, .u32, .u64, .usize, .f32, .f64, .bool, .rune]
29872987

29882988
// Check if the expression is a function argument (local variable) that needs heap allocation
2989-
is_fn_arg := if expr is ast.Ident && expr.obj is ast.Var {
2990-
expr.obj.is_arg
2991-
} else {
2992-
false
2993-
}
2989+
is_fn_arg := expr.is_fn_arg()
29942990

29952991
if !is_cast_fixed_array_init && (is_comptime_variant || !expr.is_lvalue()
29962992
|| (expr is ast.Ident && (expr.obj.is_simple_define_const()
29972993
|| (expr.obj is ast.Var && expr.obj.is_index_var)))
29982994
|| is_primitive_to_interface || is_fn_arg) {
29992995
// Note: the `_to_sumtype_` family of functions do call memdup internally, making
3000-
// another duplicate with the HEAP macro is redundant, so use ADDR instead:
2996+
// another duplicate with the HEAP macro is redundant, so use ADDR instead.
2997+
// However, this only applies when the expression is a simple Ident that we can
2998+
// easily declare a temporary variable for. For SelectorExpr and IndexExpr, we
2999+
// cannot use ADDR because it requires a temporary variable declaration.
3000+
// For sumtype casts with SelectorExpr or IndexExpr, we should not use any macro
3001+
// because the sumtype casting function already handles memory allocation.
30013002
if expr.is_as_cast() {
30023003
if !got_is_ptr && expr is ast.SelectorExpr {
30033004
// (var as Type).field_non_ptr
@@ -3008,8 +3009,15 @@ fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp ast.Ty
30083009
defer(fn) {
30093010
g.inside_smartcast = old_inside_smartcast
30103011
}
3012+
} else if is_sumtype_cast && is_fn_arg && expr !is ast.Ident {
3013+
// For sumtype casts with SelectorExpr or IndexExpr, don't use any macro
3014+
// because the sumtype casting function already handles memory allocation
3015+
// and we cannot use ADDR for these expression types.
3016+
// Just take the address of the expression since the function expects a pointer.
3017+
g.write('&(')
3018+
rparen_n++
30113019
} else {
3012-
promotion_macro_name := if is_sumtype_cast { 'ADDR' } else { 'HEAP' }
3020+
promotion_macro_name := if is_sumtype_cast && is_fn_arg { 'ADDR' } else { 'HEAP' }
30133021
g.write('${promotion_macro_name}(${got_styp}, (')
30143022
rparen_n += 2
30153023
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
module main
2+
3+
import arrays
4+
5+
interface Value {}
6+
7+
struct Inner {
8+
data string
9+
}
10+
11+
struct Outer {
12+
inner Inner
13+
}
14+
15+
struct WithArray {
16+
items []string
17+
}
18+
19+
fn nested_struct_field(p Outer) []Value {
20+
mut params := []Value{}
21+
if p.inner.data != '' {
22+
params = arrays.concat(params, p.inner.data)
23+
}
24+
return params
25+
}
26+
27+
fn array_index_field(p WithArray) []Value {
28+
mut params := []Value{}
29+
if p.items.len > 0 {
30+
params = arrays.concat(params, p.items[0])
31+
}
32+
return params
33+
}
34+
35+
fn test_interface_nested_field_ref_arg() {
36+
outer := Outer{
37+
inner: Inner{
38+
data: 'nested data'
39+
}
40+
}
41+
params := nested_struct_field(outer)
42+
assert params == [Value('nested data')]
43+
}
44+
45+
fn test_interface_array_index_ref_arg() {
46+
wa := WithArray{
47+
items: ['first', 'second']
48+
}
49+
params := array_index_field(wa)
50+
assert params == [Value('first')]
51+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module main
2+
3+
import arrays
4+
5+
interface Value {}
6+
7+
struct SomeStruct {
8+
s string
9+
}
10+
11+
fn generate_params(p SomeStruct) (string, []Value) {
12+
mut conditions := []string{}
13+
mut params := []Value{}
14+
15+
if p.s != '' {
16+
conditions = arrays.concat(conditions, 'email = ?')
17+
params = arrays.concat(params, p.s)
18+
}
19+
20+
return conditions.join('AND '), params
21+
}
22+
23+
fn test_interface_struct_field_ref_arg() {
24+
str, params := generate_params(SomeStruct{
25+
s: 'some string'
26+
})
27+
assert str == 'email = ?'
28+
assert params == [Value('some string')]
29+
}

0 commit comments

Comments
 (0)