checker: infer generic interface type in `i := Interface(Struct<u32>{})`

pull/13004/head
Delyan Angelov 2021-12-30 21:19:24 +02:00
parent ae036b6146
commit b10ff1e41b
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
6 changed files with 74 additions and 7 deletions

View File

@ -3369,13 +3369,14 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
// node.expr_type: `Inside`
// node.typ: `Outside`
node.expr_type = c.expr(node.expr) // type to be casted
mut from_type := node.expr_type
to_type := node.typ
mut from_type := node.expr_type
from_sym := c.table.sym(from_type)
final_from_sym := c.table.final_sym(from_type)
to_sym := c.table.sym(to_type) // type to be used as cast
final_to_sym := c.table.final_sym(to_type)
mut to_type := node.typ
mut to_sym := c.table.sym(to_type) // type to be used as cast
mut final_to_sym := c.table.final_sym(to_type)
if (to_sym.is_number() && from_sym.name == 'JS.Number')
|| (to_sym.is_number() && from_sym.name == 'JS.BigInt')
@ -3435,6 +3436,14 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
&& !c.inside_unsafe {
c.mark_as_referenced(mut &node.expr, true)
}
if (to_sym.info as ast.Interface).is_generic {
inferred_type := c.resolve_generic_interface(from_type, to_type, node.pos)
if inferred_type != 0 {
to_type = inferred_type
to_sym = c.table.sym(to_type)
final_to_sym = c.table.final_sym(to_type)
}
}
}
} else if to_type == ast.bool_type && from_type != ast.bool_type && !c.inside_unsafe {
c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos)

View File

@ -186,11 +186,27 @@ fn (mut c Checker) resolve_generic_interface(typ ast.Type, interface_type ast.Ty
}
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{} }
typ_sym.find_method_with_generic_parent(imethod.name) or {
c.error('can not find method `$imethod.name` on `$typ_sym.name`, needed for interface: `$inter_sym.name`',
pos)
return 0
}
}
if imethod.return_type.has_flag(.generic) {
imret_sym := c.table.sym(imethod.return_type)
mret_sym := c.table.sym(method.return_type)
if method.return_type == ast.void_type
&& imethod.return_type != method.return_type {
c.error('interface method `$imethod.name` returns `$imret_sym.name`, but implementation method `$method.name` returns no value',
pos)
return 0
}
if imethod.return_type == ast.void_type
&& imethod.return_type != method.return_type {
c.error('interface method `$imethod.name` returns no value, but implementation method `$method.name` returns `$mret_sym.name`',
pos)
return 0
}
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)
@ -215,7 +231,8 @@ fn (mut c Checker) resolve_generic_interface(typ ast.Type, interface_type ast.Ty
}
}
if inferred_type == ast.void_type {
c.error('could not infer generic type `$gt_name` in interface', pos)
c.error('could not infer generic type `$gt_name` in interface `$inter_sym.name`',
pos)
return interface_type
}
inferred_types << inferred_type

View File

@ -0,0 +1,11 @@
vlib/v/checker/tests/generic_interface_err.vv:9:6: error: generic struct init must specify type parameter, e.g. Foo<int>
7 | }
8 |
9 | s := Struct{7}
| ~~~~~~~~~
10 | i := Interface(s)
vlib/v/checker/tests/generic_interface_err.vv:10:6: error: can not find method `method` on `Struct`, needed for interface: `Interface`
8 |
9 | s := Struct{7}
10 | i := Interface(s)
| ~~~~~~~~~~~~

View File

@ -0,0 +1,10 @@
struct Struct<T> {
value int
}
interface Interface<T> {
method() T
}
s := Struct{7}
i := Interface(s)

View File

@ -4,7 +4,7 @@ vlib/v/checker/tests/interface_generic_err.vv:7:9: error: generic struct init mu
7 | what := What{}
| ~~~~~~
8 | why := Why(what)
vlib/v/checker/tests/interface_generic_err.vv:8:8: error: could not infer generic type `T` in interface
vlib/v/checker/tests/interface_generic_err.vv:8:8: error: could not infer generic type `T` in interface `Why`
6 | // no segfault without generic
7 | what := What{}
8 | why := Why(what)

View File

@ -0,0 +1,20 @@
struct Struct<T> {
value int
x T
}
fn (s Struct<T>) method() T {
return s.x + s.x
}
interface Interface<T> {
method() T
}
fn test_infer_generic_interface() {
s := Struct<u32>{7, 5}
println(s)
i := Interface(s)
println(i)
assert i.method() == 10
}