Skip to content

Commit

Permalink
ast: fix generic concrete of struct container type fields (fix #18852) (
Browse files Browse the repository at this point in the history
  • Loading branch information
shove70 committed Dec 14, 2023
1 parent f303679 commit 0d2ed22
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 3 deletions.
51 changes: 49 additions & 2 deletions vlib/v/ast/table.v
Original file line number Diff line number Diff line change
Expand Up @@ -1736,7 +1736,7 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name
{
gts := t.sym(ct)
if ct.is_ptr() {
nrt += '&'
nrt += '&'.repeat(ct.nr_muls())
}
nrt += gts.name
rnrt += gts.name
Expand Down Expand Up @@ -1915,7 +1915,7 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
{
gts := t.sym(ct)
if ct.is_ptr() {
nrt += '&'
nrt += '&'.repeat(ct.nr_muls())
}
nrt += gts.name
c_nrt += gts.cname
Expand Down Expand Up @@ -1947,6 +1947,12 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
{
fields[i].typ = t_typ
}
if fields[i].typ.has_flag(.generic)
&& sym.kind in [.array, .array_fixed, .map]
&& t.check_if_elements_need_unwrap(typ, fields[i].typ) {
fields[i].typ = t.unwrap_generic_type(fields[i].typ, t_generic_names,
t_concrete_types)
}
}
// Update type in `info.embeds`, if it's embed
if fields[i].name.len > 1 && fields[i].name[0].is_capital() {
Expand Down Expand Up @@ -2327,6 +2333,47 @@ pub fn (t &Table) get_generic_names(generic_types []Type) []string {
return generic_names
}

// check_if_elements_need_unwrap checks if the elements of a container (arrays, maps) need to be unwrapped to a concrete type
pub fn (mut t Table) check_if_elements_need_unwrap(root_typ Type, typ Type) bool {
sym := t.sym(typ)
if sym.kind !in [.array, .array_fixed, .map] {
return false
}

mut typs := []Type{}
match sym.info {
Array {
typs << (sym.info as Array).elem_type
}
ArrayFixed {
typs << (sym.info as ArrayFixed).elem_type
}
Map {
typs << (sym.info as Map).key_type
typs << (sym.info as Map).value_type
}
else {}
}
for typ_ in typs {
if typ_.has_flag(.generic) {
t_sym := t.sym(typ_)
match t_sym.info {
Struct, Interface, SumType {
if t_sym.info.is_generic && t_sym.info.generic_types.len > 0
&& t_sym.info.concrete_types.len == 0 && typ_.idx() != root_typ.idx() {
return true
}
}
else {}
}
}
if t.check_if_elements_need_unwrap(root_typ, typ_) {
return true
}
}
return false
}

pub fn (t &Table) is_comptime_type(x Type, y ComptimeType) bool {
x_kind := t.type_kind(x)
match y.kind {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
struct Root[T] {
mut:
edge1 Edge[T]
edge2 map[int]Edge[T]
edge3 map[int][]Edge[T]
edge4 []Edge[T]
edge5 [][]Edge[T]
edge6 [1]Edge[T]
edge7 []T
edge8 [][]T
edge9 [1]T
}

struct Edge[T] {
}

fn new[T]() Root[T] {
return Root[T]{}
}

// for issue 18852
fn test_generics_struct_field_with_generics_struct_elements() {
mut root := new[int]()

assert root.edge1 == Edge[int]{}

root.edge2[1] = Edge[int]{}
assert root.edge2[1] == Edge[int]{}

root.edge3[1] = []Edge[int]{len: 1}
assert root.edge3[1] == [Edge[int]{}]

root.edge4 << Edge[int]{}
assert root.edge4 == [Edge[int]{}]

root.edge5 << root.edge4
assert root.edge5 == [[Edge[int]{}]]

assert root.edge6 == [Edge[int]{}]!

root.edge7 << 1
assert root.edge7 == [1]

root.edge8 << root.edge7
assert root.edge8 == [[1]]

assert root.edge9 == [0]!
}
2 changes: 1 addition & 1 deletion vlib/v/tests/interface_embedding_call_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,6 @@ fn (d DerivedStruct[T]) method[T](arg T) T {
}

fn main() {
a := IFoo[int](DerivedStruct[int]{})
a := IBaz[int](DerivedStruct[int]{})
assert a.method(1) == 1
}

0 comments on commit 0d2ed22

Please sign in to comment.