Skip to content

Commit d2e291e

Browse files
authored
checker,table,cgen: fix generic interface confusion (fix #25478) (#26181)
1 parent f6810fd commit d2e291e

5 files changed

Lines changed: 93 additions & 7 deletions

File tree

‎vlib/v/ast/table.v‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2327,7 +2327,7 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co
23272327
}
23282328
}
23292329
}
2330-
mut all_methods := unsafe { ts.methods }
2330+
mut all_methods := ts.methods.clone()
23312331
for imethod in imethods {
23322332
for mut method in all_methods {
23332333
if imethod.name == method.name {
@@ -2353,6 +2353,9 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co
23532353
for method in all_methods {
23542354
ts_copy.register_method(method)
23552355
}
2356+
if final_concrete_types.len > 0 {
2357+
t.unwrap_method_types(ts, generic_names, concrete_types, final_concrete_types)
2358+
}
23562359
return new_type(new_idx).derive(typ).clear_flag(.generic)
23572360
}
23582361
else {}
@@ -2485,7 +2488,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
24852488
}
24862489
sym.register_method(method)
24872490
}
2488-
mut all_methods := unsafe { parent.methods }
2491+
mut all_methods := parent.methods.clone()
24892492
for imethod in imethods {
24902493
for mut method in all_methods {
24912494
if imethod.name == method.name {

‎vlib/v/checker/interface.v‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,14 @@ fn (mut c Checker) unwrap_generic_interface(typ ast.Type, interface_type ast.Typ
382382
c.table.fn_generic_types[im_fkey] << inferred_types
383383
}
384384
}
385-
inter_sym.info.concrete_types = inferred_types
386-
return c.table.unwrap_generic_type(interface_type, generic_names, inter_sym.info.concrete_types)
385+
result_type := c.table.unwrap_generic_type(interface_type, generic_names,
386+
inferred_types)
387+
// Set concrete types on the instantiated interface symbol
388+
mut result_sym := c.table.sym(result_type)
389+
if mut result_sym.info is ast.Interface {
390+
result_sym.info.concrete_types = inferred_types
391+
}
392+
return result_type
387393
}
388394
}
389395
return interface_type

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1529,8 +1529,21 @@ fn (mut g Gen) unwrap_receiver_type(node ast.CallExpr) (ast.Type, &ast.TypeSymbo
15291529
generic_names := sym.info.generic_types.map(g.table.sym(it).name)
15301530
// see comment at top of vlib/v/gen/c/utils.v
15311531
mut muttable := unsafe { &ast.Table(g.table) }
1532+
// If receiver_type is the generic parent but left_type is an instantiated version,
1533+
// use left_type's concrete_types for the conversion
1534+
mut concrete_types := sym.info.concrete_types.clone()
1535+
if concrete_types.len == 0 && generic_names.len > 0 {
1536+
left_sym := g.table.sym(left_type)
1537+
if left_sym.info is ast.Interface {
1538+
concrete_types = left_sym.info.concrete_types.clone()
1539+
} else if left_sym.info is ast.Struct {
1540+
concrete_types = left_sym.info.concrete_types.clone()
1541+
} else if left_sym.info is ast.SumType {
1542+
concrete_types = left_sym.info.concrete_types.clone()
1543+
}
1544+
}
15321545
if utyp := muttable.convert_generic_type(node.receiver_type, generic_names,
1533-
sym.info.concrete_types)
1546+
concrete_types)
15341547
{
15351548
unwrapped_rec_type = utyp
15361549
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Test for issue #25478
2+
// Using the same generic interface with different type parameters in a function
3+
// should correctly resolve the return types of interface methods.
4+
5+
interface Bug[T] {
6+
buggy() T
7+
}
8+
9+
struct StringBug implements Bug[string] {
10+
value string
11+
}
12+
13+
fn (s StringBug) buggy() string {
14+
return s.value
15+
}
16+
17+
struct StringArrayBug implements Bug[[]string] {
18+
values []string
19+
}
20+
21+
fn (s StringArrayBug) buggy() []string {
22+
return s.values
23+
}
24+
25+
fn list(items Bug[[]string], selected Bug[string], generator fn (arg string, selected bool) string) []string {
26+
value := items.buggy() // should be []string
27+
children := value.map(generator(it, it == selected.buggy()))
28+
return children
29+
}
30+
31+
fn test_generic_interface_with_different_type_params() {
32+
items := StringArrayBug{
33+
values: ['Hello', 'Hola']
34+
}
35+
selected := StringBug{
36+
value: 'Hola'
37+
}
38+
result := list(items, selected, fn (arg string, is_selected bool) string {
39+
if is_selected {
40+
return '--${arg}'
41+
}
42+
return arg
43+
})
44+
assert result.len == 2
45+
assert result[0] == 'Hello'
46+
assert result[1] == '--Hola'
47+
}
48+
49+
fn test_generic_interface_method_return_types() {
50+
items := StringArrayBug{
51+
values: ['a', 'b', 'c']
52+
}
53+
selected := StringBug{
54+
value: 'b'
55+
}
56+
57+
// Verify return types are correctly resolved
58+
arr := items.buggy()
59+
assert arr.len == 3
60+
assert arr[0] == 'a'
61+
62+
str := selected.buggy()
63+
assert str == 'b'
64+
}

‎vlib/x/sessions/store.v‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ mut:
1313
}
1414

1515
// get data from all sessions, optional to implement.
16-
pub fn (mut s Store) all[T]() ![]T {
16+
pub fn (mut s Store[T]) all[T]() ![]T {
1717
return []T{}
1818
}
1919

2020
// clear all session data, optional to implement.
21-
pub fn (mut s Store) clear[T]() ! {}
21+
pub fn (mut s Store[T]) clear[T]() ! {}

0 commit comments

Comments
 (0)