diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v
index e6858794d9..3d01708eed 100644
--- a/vlib/v/ast/table.v
+++ b/vlib/v/ast/table.v
@@ -2190,16 +2190,15 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co
Interface {
// resolve generic types inside methods
mut imethods := ts.info.methods.clone()
+ gn_names := t.get_real_generic_names(typ, generic_names)
for mut method in imethods {
- if unwrap_typ := t.convert_generic_type(method.return_type, generic_names,
- concrete_types)
+ if unwrap_typ := t.convert_generic_type(method.return_type, gn_names,
+ concrete_types[..gn_names.len])
{
method.return_type = unwrap_typ
}
for mut param in method.params {
- if unwrap_typ := t.convert_generic_type(param.typ, generic_names,
- concrete_types)
- {
+ if unwrap_typ := t.convert_generic_type(param.typ, gn_names, concrete_types) {
param.typ = unwrap_typ
}
}
@@ -2289,6 +2288,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
fields[i].typ = t.unwrap_generic_type(fields[i].typ,
generic_names, info.concrete_types)
}
+
if t_typ := t.convert_generic_type(fields[i].typ, generic_names,
info.concrete_types)
{
@@ -2464,6 +2464,18 @@ pub fn (mut t Table) generic_insts_to_concrete() {
}
}
+// Extracts all generic names from Type[B] => B when [B] is present
+// otherwise generic_names is returned
+pub fn (t &Table) get_real_generic_names(typ Type, generic_names []string) []string {
+ if typ.has_flag(.generic) {
+ typ_name := t.type_to_str(typ)
+ if typ_name.contains('>[') {
+ return typ_name.split('>[')[1].all_before_last(']').split(',')
+ }
+ }
+ return generic_names
+}
+
// Extracts all type names from given types, notice that MultiReturn will be decompose
// and will not included in returned string
pub fn (t &Table) get_generic_names(generic_types []Type) []string {
diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v
index e03e10d3a0..79cd19c325 100644
--- a/vlib/v/gen/c/cgen.v
+++ b/vlib/v/gen/c/cgen.v
@@ -1107,6 +1107,7 @@ pub fn (mut g Gen) get_sumtype_variant_name(typ ast.Type, sym ast.TypeSymbol) st
pub fn (mut g Gen) write_typeof_functions() {
g.writeln('')
g.writeln('// >> typeof() support for sum types / interfaces')
+ mut already_generated_ifaces := map[string]bool{}
for ityp, sym in g.table.type_symbols {
if sym.kind == .sum_type {
if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
@@ -1177,6 +1178,10 @@ pub fn (mut g Gen) write_typeof_functions() {
if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
continue
}
+ if sym.cname in already_generated_ifaces {
+ continue
+ }
+ already_generated_ifaces[sym.cname] = true
g.definitions.writeln('${g.static_non_parallel}char * v_typeof_interface_${sym.cname}(int sidx);')
if g.pref.parallel_cc {
g.extern_out.writeln('extern char * v_typeof_interface_${sym.cname}(int sidx);')
@@ -1805,11 +1810,15 @@ static inline void __${sym.cname}_pushval(${sym.cname} ch, ${push_arg} val) {
interface_non_generic_syms << sym
}
}
+ mut already_generated_ifaces := map[string]bool{}
for sym in interface_non_generic_syms {
if g.pref.skip_unused && sym.idx !in g.table.used_features.used_syms {
continue
}
- g.write_interface_typesymbol_declaration(sym)
+ if sym.cname !in already_generated_ifaces {
+ g.write_interface_typesymbol_declaration(sym)
+ already_generated_ifaces[sym.cname] = true
+ }
}
}
@@ -7859,6 +7868,7 @@ fn (mut g Gen) interface_table() string {
}
mut sb := strings.new_builder(100)
mut conversion_functions := strings.new_builder(100)
+ mut already_generated_ifaces := map[string]bool{}
interfaces := g.table.type_symbols.filter(it.kind == .interface && it.info is ast.Interface)
for isym in interfaces {
inter_info := isym.info as ast.Interface
@@ -7868,6 +7878,10 @@ fn (mut g Gen) interface_table() string {
if g.pref.skip_unused && isym.idx !in g.table.used_features.used_syms {
continue
}
+ if isym.cname in already_generated_ifaces {
+ continue
+ }
+ already_generated_ifaces[isym.cname] = true
// interface_name is for example Speaker
interface_name := isym.cname
// generate a struct that references interface methods
diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v
index a2156e7a49..582e0fa3c3 100644
--- a/vlib/v/markused/walker.v
+++ b/vlib/v/markused/walker.v
@@ -1573,6 +1573,7 @@ pub fn (mut w Walker) finalize(include_panic_deps bool) {
}
if (w.used_option + w.used_result + w.used_none) > 0 {
w.mark_const_as_used('none__')
+ w.fn_by_name('memdup')
}
if include_panic_deps || w.uses_external_type || w.uses_asserts || w.uses_debugger
|| w.uses_interp {
@@ -1591,6 +1592,7 @@ pub fn (mut w Walker) finalize(include_panic_deps bool) {
w.fn_by_name(ref_array_idx_str + '.push')
w.fn_by_name(string_idx_str + '.substr')
w.fn_by_name('v_fixed_index')
+ w.fn_by_name('fast_string_eq')
w.mark_by_sym_name('StrIntpData')
w.mark_by_sym_name('StrIntpMem')
}
diff --git a/vlib/v/tests/generics/generic_interface_field_test.v b/vlib/v/tests/generics/generic_interface_field_test.v
new file mode 100644
index 0000000000..752a6acb28
--- /dev/null
+++ b/vlib/v/tests/generics/generic_interface_field_test.v
@@ -0,0 +1,79 @@
+pub struct Range[T] {
+pub:
+ from T
+ to T
+ inclusive bool
+pub mut:
+ step T = T(1)
+ i T
+mut:
+ started bool
+}
+
+pub fn (mut r Range[T]) next[T]() ?T {
+ if !r.started {
+ r.started = true
+ assert r.step > 0
+ if r.from > r.to {
+ r.step = -r.step
+ }
+ r.i = r.from
+ }
+ i := r.i
+ next_i := i + r.step
+ if r.inclusive {
+ if r.step < 0 && i < r.to {
+ return none
+ }
+ if r.step > 0 && i > r.to {
+ return none
+ }
+ } else {
+ if r.step < 0 && i <= r.to {
+ return none
+ }
+ if r.step > 0 && i >= r.to {
+ return none
+ }
+ }
+ r.i = next_i
+ return i
+}
+
+pub interface Iterator[T] {
+mut:
+ next() ?T
+}
+
+pub struct Zip[A, B] {
+mut:
+ a Iterator[A]
+ b Iterator[B]
+}
+
+pub struct Pair[A, B] {
+ a A
+ b B
+}
+
+pub fn (mut z Zip[A, B]) next[A, B]() ?Pair[A, B] {
+ a := z.a.next()?
+ b := z.b.next()?
+ return Pair[A, B]{a, b}
+}
+
+fn test_main() {
+ mut r1 := Range{
+ from: 5
+ to: 10
+ }
+ _ := Iterator[int](r1)
+
+ mut r2 := Range{
+ from: 10
+ to: 5
+ }
+ _ := Iterator[int](r2)
+
+ _ := Zip[int, int]{}
+}