diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 5abb4917f3..60c81db30f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2991,8 +2991,12 @@ fn (mut c Checker) resolve_generic_interface(typ ast.Type, interface_type ast.Ty typ_sym.find_method_with_generic_parent(imethod.name) or { ast.Fn{} } } if imethod.return_type.has_flag(.generic) { - if method.return_type !in inferred_types { - inferred_types << method.return_type + mut inferred_type := method.return_type + if imethod.return_type.has_flag(.optional) { + inferred_type = inferred_type.clear_flag(.optional) + } + if inferred_type !in inferred_types { + inferred_types << inferred_type } } for i, iparam in imethod.params { @@ -3362,6 +3366,33 @@ pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { } } } + if typ.has_flag(.generic) && !has_field { + gs := c.table.get_type_symbol(typ) + if f := c.table.find_field(gs, field_name) { + has_field = true + field = f + } else { + // look for embedded field + if gs.info is ast.Struct { + mut found_fields := []ast.StructField{} + mut embed_of_found_fields := []ast.Type{} + for embed in gs.info.embeds { + embed_sym := c.table.get_type_symbol(embed) + if f := c.table.find_field(embed_sym, field_name) { + found_fields << f + embed_of_found_fields << embed + } + } + if found_fields.len == 1 { + field = found_fields[0] + has_field = true + node.from_embed_type = embed_of_found_fields[0] + } else if found_fields.len > 1 { + c.error('ambiguous field `$field_name`', node.pos) + } + } + } + } } if has_field { if sym.mod != c.mod && !field.is_pub && sym.language != .c { diff --git a/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out b/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out index 3453697965..502f2b816a 100644 --- a/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out +++ b/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out @@ -5,13 +5,6 @@ vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:26:1: error: g | ~~~~~~~~~~~~~~~~~~~~~~~~~ 27 | t := <-g.ch 28 | handle(t) -vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:27:7: error: <- operator can only be used with `chan` types - 25 | - 26 | fn g_worker(g Generic) { - 27 | t := <-g.ch - | ~~ - 28 | handle(t) - 29 | // println("${t.msg}") vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:32:1: error: generic function declaration must specify generic type names, e.g. foo 30 | } 31 | diff --git a/vlib/v/tests/generics_fn_return_generic_interface_test.v b/vlib/v/tests/generics_fn_return_generic_interface_test.v new file mode 100644 index 0000000000..aededf0ed5 --- /dev/null +++ b/vlib/v/tests/generics_fn_return_generic_interface_test.v @@ -0,0 +1,28 @@ +interface Iter { + next() ?T +} + +struct ArrayIter { + data []T +} + +fn (mut i ArrayIter) next() ?T { + if i.data.len == 0 { + return none + } + return i.data[0] +} + +fn iter(arr []T) Iter { + return ArrayIter{ + data: arr + } +} + +fn test_generics_fn_return_generic_interface() { + x := iter([1, 2, 3]) + println(x) + y := x.next() or { 0 } + println(y) + assert y == 1 +}