mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
checker: fix generics interface with multi generic types (#10964)
This commit is contained in:
parent
b666482d84
commit
4cf91a28f6
@ -3023,58 +3023,64 @@ fn (mut c Checker) resolve_generic_interface(typ ast.Type, interface_type ast.Ty
|
|||||||
utyp := c.unwrap_generic(typ)
|
utyp := c.unwrap_generic(typ)
|
||||||
typ_sym := c.table.get_type_symbol(utyp)
|
typ_sym := c.table.get_type_symbol(utyp)
|
||||||
mut inter_sym := c.table.get_type_symbol(interface_type)
|
mut inter_sym := c.table.get_type_symbol(interface_type)
|
||||||
|
|
||||||
if mut inter_sym.info is ast.Interface {
|
if mut inter_sym.info is ast.Interface {
|
||||||
if inter_sym.info.is_generic {
|
if inter_sym.info.is_generic {
|
||||||
mut inferred_types := []ast.Type{}
|
mut inferred_types := []ast.Type{}
|
||||||
for ifield in inter_sym.info.fields {
|
generic_names := inter_sym.info.generic_types.map(c.table.get_type_name(it))
|
||||||
if ifield.typ.has_flag(.generic) {
|
// inferring interface generic types
|
||||||
if field := c.table.find_field_with_embeds(typ_sym, ifield.name) {
|
for gt_name in generic_names {
|
||||||
if field.typ !in inferred_types {
|
mut inferred_type := ast.void_type
|
||||||
inferred_types << field.typ
|
for ifield in inter_sym.info.fields {
|
||||||
|
if ifield.typ.has_flag(.generic) && c.table.get_type_name(ifield.typ) == gt_name {
|
||||||
|
if field := c.table.find_field_with_embeds(typ_sym, ifield.name) {
|
||||||
|
inferred_type = field.typ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for imethod in inter_sym.info.methods {
|
||||||
|
method := typ_sym.find_method(imethod.name) or {
|
||||||
|
typ_sym.find_method_with_generic_parent(imethod.name) or { ast.Fn{} }
|
||||||
|
}
|
||||||
|
if imethod.return_type.has_flag(.generic) {
|
||||||
|
imret_sym := c.table.get_type_symbol(imethod.return_type)
|
||||||
|
mret_sym := c.table.get_type_symbol(method.return_type)
|
||||||
|
if imret_sym.info is ast.MultiReturn && mret_sym.info is ast.MultiReturn {
|
||||||
|
for i, mr_typ in imret_sym.info.types {
|
||||||
|
if mr_typ.has_flag(.generic)
|
||||||
|
&& c.table.get_type_name(mr_typ) == gt_name {
|
||||||
|
inferred_type = mret_sym.info.types[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if c.table.get_type_name(imethod.return_type) == gt_name {
|
||||||
|
mut ret_typ := method.return_type
|
||||||
|
if imethod.return_type.has_flag(.optional) {
|
||||||
|
ret_typ = ret_typ.clear_flag(.optional)
|
||||||
|
}
|
||||||
|
inferred_type = ret_typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, iparam in imethod.params {
|
||||||
|
param := method.params[i] or { ast.Param{} }
|
||||||
|
if iparam.typ.has_flag(.generic)
|
||||||
|
&& c.table.get_type_name(iparam.typ) == gt_name {
|
||||||
|
inferred_type = param.typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if inferred_type == ast.void_type {
|
||||||
|
c.error('could not infer generic type `$gt_name` in interface', pos)
|
||||||
|
return interface_type
|
||||||
|
}
|
||||||
|
inferred_types << inferred_type
|
||||||
}
|
}
|
||||||
|
// add concrete types to method
|
||||||
for imethod in inter_sym.info.methods {
|
for imethod in inter_sym.info.methods {
|
||||||
method := typ_sym.find_method(imethod.name) or {
|
|
||||||
typ_sym.find_method_with_generic_parent(imethod.name) or { ast.Fn{} }
|
|
||||||
}
|
|
||||||
if imethod.return_type.has_flag(.generic) {
|
|
||||||
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 {
|
|
||||||
param := method.params[i] or { ast.Param{} }
|
|
||||||
if iparam.typ.has_flag(.generic) {
|
|
||||||
if param.typ !in inferred_types {
|
|
||||||
inferred_types << param.typ
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if inferred_types !in c.table.fn_generic_types[imethod.name] {
|
if inferred_types !in c.table.fn_generic_types[imethod.name] {
|
||||||
c.table.fn_generic_types[imethod.name] << inferred_types
|
c.table.fn_generic_types[imethod.name] << inferred_types
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if inferred_types.len == 0 {
|
inter_sym.info.concrete_types = inferred_types
|
||||||
c.error('cannot infer generic types for ${c.table.type_to_str(interface_type)}',
|
|
||||||
pos)
|
|
||||||
return ast.void_type
|
|
||||||
}
|
|
||||||
if inferred_types.len > 1 {
|
|
||||||
c.error('cannot infer generic types for ${c.table.type_to_str(interface_type)}: got conflicting type information',
|
|
||||||
pos)
|
|
||||||
return ast.void_type
|
|
||||||
}
|
|
||||||
inferred_type := inferred_types[0]
|
|
||||||
if inferred_type !in inter_sym.info.concrete_types {
|
|
||||||
inter_sym.info.concrete_types << inferred_type
|
|
||||||
}
|
|
||||||
generic_names := inter_sym.info.generic_types.map(c.table.get_type_name(it))
|
|
||||||
return c.unwrap_generic_type(interface_type, generic_names, inter_sym.info.concrete_types)
|
return c.unwrap_generic_type(interface_type, generic_names, inter_sym.info.concrete_types)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
interface Iter<T, U> {
|
||||||
|
next() ?(T, U)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ArrIter<T, U> {
|
||||||
|
t []T
|
||||||
|
u []U
|
||||||
|
mut:
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut it ArrIter<T, U>) next<T, U>() ?(T, U) {
|
||||||
|
if it.index >= it.t.len || it.index >= it.u.len {
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
defer {
|
||||||
|
it.index++
|
||||||
|
}
|
||||||
|
return it.t[it.index], it.u[it.index]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter<T, U>(t []T, u []U) Iter<T, U> {
|
||||||
|
return ArrIter<T,U>{
|
||||||
|
t: t
|
||||||
|
u: u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_generics_interface_with_multi_generic_types() {
|
||||||
|
mut x := iter<int, string>([1, 2, 3], ['foo', 'bar', 'baz'])
|
||||||
|
ret := 0
|
||||||
|
a, b := x.next() or { ret, '' }
|
||||||
|
println(a)
|
||||||
|
println(b)
|
||||||
|
assert a == 1
|
||||||
|
assert b == 'foo'
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user