From 06327c80540963b96a2f3eb93e7cee39db189afb Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Thu, 28 Aug 2025 09:39:36 -0300 Subject: [PATCH] fix --- vlib/v/ast/table.v | 22 ++++-- vlib/v/gen/c/cgen.v | 16 +++- vlib/v/markused/walker.v | 2 + .../generics/generic_interface_field_test.v | 79 +++++++++++++++++++ 4 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 vlib/v/tests/generics/generic_interface_field_test.v 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]{} +}