diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index d845a26a359034..0e859134f18931 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1841,6 +1841,10 @@ pub fn (mut t Table) generic_type_names(generic_type Type) []string { } pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concrete_types []Type) Type { + return t.unwrap_generic_type_ex(typ, generic_names, concrete_types, false) +} + +pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, concrete_types []Type, recheck_concrete_types bool) Type { mut final_concrete_types := []Type{} mut fields := []StructField{} mut needs_unwrap_types := []Type{} @@ -1850,12 +1854,14 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr match ts.info { Array { dims, elem_type := t.get_array_dims(ts.info) - unwrap_typ := t.unwrap_generic_type(elem_type, generic_names, concrete_types) + unwrap_typ := t.unwrap_generic_type_ex(elem_type, generic_names, concrete_types, + recheck_concrete_types) idx := t.find_or_register_array_with_dims(unwrap_typ, dims) return new_type(idx).derive_add_muls(typ).clear_flag(.generic) } ArrayFixed { - unwrap_typ := t.unwrap_generic_type(ts.info.elem_type, generic_names, concrete_types) + unwrap_typ := t.unwrap_generic_type_ex(ts.info.elem_type, generic_names, concrete_types, + recheck_concrete_types) idx := t.find_or_register_array_fixed(unwrap_typ, ts.info.size, None{}, false) return new_type(idx).derive_add_muls(typ).clear_flag(.generic) } @@ -1865,15 +1871,16 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr return new_type(idx).derive_add_muls(typ).clear_flag(.generic) } Thread { - unwrap_typ := t.unwrap_generic_type(ts.info.return_type, generic_names, concrete_types) + unwrap_typ := t.unwrap_generic_type_ex(ts.info.return_type, generic_names, + concrete_types, recheck_concrete_types) idx := t.find_or_register_thread(unwrap_typ) return new_type(idx).derive_add_muls(typ).clear_flag(.generic) } Map { - unwrap_key_type := t.unwrap_generic_type(ts.info.key_type, generic_names, - concrete_types) - unwrap_value_type := t.unwrap_generic_type(ts.info.value_type, generic_names, - concrete_types) + unwrap_key_type := t.unwrap_generic_type_ex(ts.info.key_type, generic_names, + concrete_types, recheck_concrete_types) + unwrap_value_type := t.unwrap_generic_type_ex(ts.info.value_type, generic_names, + concrete_types, recheck_concrete_types) idx := t.find_or_register_map(unwrap_key_type, unwrap_value_type) return new_type(idx).derive_add_muls(typ).clear_flag(.generic) } @@ -1925,6 +1932,20 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr nrt += ']' idx := t.type_idxs[nrt] if idx != 0 && t.type_symbols[idx].kind != .placeholder { + if recheck_concrete_types { + fields = ts.info.fields.clone() + for i in 0 .. fields.len { + if !fields[i].typ.has_flag(.generic) { + continue + } + // Map[T], []Type[T] + if t.type_kind(fields[i].typ) in [.array, .array_fixed, .map] + && t.check_if_elements_need_unwrap(typ, fields[i].typ) { + t.unwrap_generic_type_ex(fields[i].typ, t_generic_names, t_concrete_types, + recheck_concrete_types) + } + } + } return new_type(idx).derive(typ).clear_flag(.generic) } else { // fields type translate to concrete type diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 87731077345819..147abe135351b7 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4988,7 +4988,7 @@ fn (mut c Checker) fetch_field_name(field ast.StructField) string { return name } -fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos token.Pos, is_container bool) bool { +fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos token.Pos, is_container_typ bool, is_generic_container bool) bool { if typ == 0 { c.error('unknown type', pos) return false @@ -5015,12 +5015,12 @@ fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos toke .function { fn_info := sym.info as ast.FnType if !c.ensure_generic_type_specify_type_names(fn_info.func.return_type, fn_info.func.return_type_pos, - is_container) { + is_container_typ, is_generic_container) { return false } for param in fn_info.func.params { if !c.ensure_generic_type_specify_type_names(param.typ, param.type_pos, - is_container) { + is_container_typ, is_generic_container) { return false } } @@ -5032,29 +5032,29 @@ fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos toke } .array { if !c.ensure_generic_type_specify_type_names((sym.info as ast.Array).elem_type, - pos, true) { + pos, true, typ.has_flag(.generic)) { return false } } .array_fixed { if !c.ensure_generic_type_specify_type_names((sym.info as ast.ArrayFixed).elem_type, - pos, true) { + pos, true, typ.has_flag(.generic)) { return false } } .map { info := sym.info as ast.Map - if !c.ensure_generic_type_specify_type_names(info.key_type, pos, true) { + if !c.ensure_generic_type_specify_type_names(info.key_type, pos, true, typ.has_flag(.generic)) { return false } - if !c.ensure_generic_type_specify_type_names(info.value_type, pos, true) { + if !c.ensure_generic_type_specify_type_names(info.value_type, pos, true, typ.has_flag(.generic)) { return false } } .sum_type { info := sym.info as ast.SumType - if info.generic_types.len > 0 && (is_container || !typ.has_flag(.generic)) - && info.concrete_types.len == 0 { + if info.generic_types.len > 0 && ((is_container_typ && !is_generic_container) + || !typ.has_flag(.generic)) && info.concrete_types.len == 0 { c.error('`${sym.name}` type is generic sumtype, must specify the generic type names, e.g. ${sym.name}[T], ${sym.name}[int]', pos) return false @@ -5078,7 +5078,8 @@ fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos toke } .alias { info := sym.info as ast.Alias - if !c.ensure_generic_type_specify_type_names(info.parent_type, pos, is_container) { + if !c.ensure_generic_type_specify_type_names(info.parent_type, pos, is_container_typ, + is_generic_container) { return false } } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index d761d74d7b360f..5734f48bfb7db2 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -135,6 +135,15 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { node.return_type_pos) } } + if gs.kind == .struct_ && c.needs_unwrap_generic_type(node.return_type) { + // resolve generic Array[T], Map[T] generics, avoid recursive generic resolving type + if c.ensure_generic_type_specify_type_names(node.return_type, node.return_type_pos, + false, false) + { + c.table.unwrap_generic_type_ex(node.return_type, c.table.cur_fn.generic_names, + c.table.cur_concrete_types, true) + } + } } return_sym := c.table.sym(node.return_type) if return_sym.info is ast.Alias { diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 7643ce34e76768..89e3ebb171384a 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -158,7 +158,11 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) { if !c.ensure_type_exists(field.typ, field.type_pos) { continue } - if !c.ensure_generic_type_specify_type_names(field.typ, field.type_pos, false) { + if !c.ensure_generic_type_specify_type_names(field.typ, field.type_pos, c.table.final_sym(field.typ).kind in [ + .array, + .array_fixed, + .map, + ], field.typ.has_flag(.generic)) { continue } if field.typ.has_flag(.generic) { @@ -506,7 +510,8 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini } // register generic struct type when current fn is generic fn if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0 { - c.table.unwrap_generic_type(node.typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types) + c.table.unwrap_generic_type_ex(node.typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types, + true) } if !is_field_zero_struct_init { c.ensure_type_exists(node.typ, node.pos) diff --git a/vlib/v/tests/structs/struct_array_generic_field_test.v b/vlib/v/tests/structs/struct_array_generic_field_test.v new file mode 100644 index 00000000000000..4db236c754ccef --- /dev/null +++ b/vlib/v/tests/structs/struct_array_generic_field_test.v @@ -0,0 +1,62 @@ +// callback types +type CBnoret[T] = fn (val T) + +type CBnoret2[T] = fn (val T, prev T) + +type CBvret[T] = fn (val T) T + +type CBvret2[T] = fn (val T, prev T) T + +type Callback[T] = CBnoret[T] | CBnoret2[T] | CBvret[T] | CBvret2[T] + +interface IObv[T] { + v T + prev T + cb []Callback[T] +} + +struct Obv[T] { +mut: + v T + prev T + cb []Callback[T] +} + +fn (o Obv[T]) get() T { + return o.v +} + +fn (o Obv[T]) do_callbacks() T { + return o.v +} + +fn (mut o Obv[T]) set(new_value T) T { + prev := o.v + if prev != new_value { + o.v = new_value + o.prev = prev + } + + return o.v +} + +fn oo2[T](init T) ?Obv[T] { + return none +} + +fn oo[T](init T) Obv[T] { + return Obv[T]{ + v: init + } +} + +fn test_main() { + one := oo(1) + txt := oo('lala') + + println('txt: ${txt.get()}') + println('one: ${one.get()}') + + assert txt.get() == 'lala' + assert one.get() == 1 +}