Skip to content

Commit b29ac57

Browse files
authored
cgen: autofree fix generic math.min[T] used with time.Time (fix #26575) (made with copilot) (#26577)
1 parent ba74039 commit b29ac57

2 files changed

Lines changed: 80 additions & 2 deletions

File tree

‎vlib/v/gen/c/if.v‎

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,40 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
205205
} else {
206206
node.typ
207207
}
208-
mut styp := g.styp(node_typ)
208+
// For generic functions, if the if-expression's type was set to a concrete type
209+
// by the checker but we're generating a different generic instance, we need to
210+
// use the correct concrete type from cur_concrete_types
211+
resolved_typ := if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0
212+
&& g.cur_concrete_types.len > 0 {
213+
// Try to unwrap generic, and if that doesn't work, check if we should use
214+
// the function's return type
215+
unwrapped := g.unwrap_generic(node_typ)
216+
if unwrapped == node_typ && g.cur_fn.return_type.has_flag(.generic) {
217+
// The node type didn't unwrap, but the function return type is generic
218+
// Get the unwrapped function return type for this instance
219+
mut fn_ret_typ := g.unwrap_generic(g.cur_fn.return_type)
220+
if g.inside_or_block {
221+
fn_ret_typ = fn_ret_typ.clear_option_and_result()
222+
}
223+
// Check if the function return type directly matches one of the concrete types
224+
// If it does, the if expression type should also match that concrete type
225+
fn_ret_is_direct_generic := g.cur_concrete_types.any(it == fn_ret_typ)
226+
if fn_ret_is_direct_generic && node_typ != fn_ret_typ {
227+
// The function returns T directly, and node_typ doesn't match the current T
228+
// This means node_typ is stale from another instance
229+
fn_ret_typ
230+
} else {
231+
// Either the function return type is wrapped (like !T or []T),
232+
// or node_typ is correct for this instance
233+
unwrapped
234+
}
235+
} else {
236+
unwrapped
237+
}
238+
} else {
239+
g.unwrap_generic(node_typ)
240+
}
241+
mut styp := g.styp(resolved_typ)
209242
if (g.inside_if_option || node_typ.has_flag(.option)) && !g.inside_or_block {
210243
raw_state = g.inside_if_option
211244
if node.typ != ast.void_type {
@@ -237,7 +270,7 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
237270
if tmp != '' {
238271
if node.typ == ast.void_type && g.last_if_option_type != 0 {
239272
// nested if on return stmt
240-
g.write2(g.styp(g.last_if_option_type), ' ')
273+
g.write2(g.styp(g.unwrap_generic(g.last_if_option_type)), ' ')
241274
} else {
242275
g.write('${styp} ')
243276
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Test for memory leak fix when using generics with math.min/max and autofree
2+
module main
3+
4+
import math
5+
import time
6+
7+
fn test_math_min_with_time_and_int() {
8+
// Test with time.Time
9+
t1 := time.Time{
10+
year: 2022
11+
day: 1
12+
}
13+
t2 := time.Time{
14+
year: 2021
15+
day: 1
16+
}
17+
t3 := math.min(t1, t2)
18+
assert t3.year == 2021
19+
20+
// Test with int
21+
a := 10
22+
b := 5
23+
c := math.min(a, b)
24+
assert c == 5
25+
}
26+
27+
fn test_math_max_with_time_and_int() {
28+
// Test with time.Time
29+
t1 := time.Time{
30+
year: 2022
31+
day: 1
32+
}
33+
t2 := time.Time{
34+
year: 2021
35+
day: 1
36+
}
37+
t3 := math.max(t1, t2)
38+
assert t3.year == 2022
39+
40+
// Test with int
41+
a := 10
42+
b := 5
43+
c := math.max(a, b)
44+
assert c == 10
45+
}

0 commit comments

Comments
 (0)