From b10ff1e41b32afd8a8cd1fad246c121d9dba47ff Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Thu, 30 Dec 2021 21:19:24 +0200 Subject: [PATCH] checker: infer generic interface type in `i := Interface(Struct{})` --- vlib/v/checker/checker.v | 17 +++++++++++---- vlib/v/checker/interface.v | 21 +++++++++++++++++-- .../v/checker/tests/generic_interface_err.out | 11 ++++++++++ vlib/v/checker/tests/generic_interface_err.vv | 10 +++++++++ .../v/checker/tests/interface_generic_err.out | 2 +- vlib/v/tests/generic_interface_infer_test.v | 20 ++++++++++++++++++ 6 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 vlib/v/checker/tests/generic_interface_err.out create mode 100644 vlib/v/checker/tests/generic_interface_err.vv create mode 100644 vlib/v/tests/generic_interface_infer_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 15f9b64b50..2ad90a7977 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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) diff --git a/vlib/v/checker/interface.v b/vlib/v/checker/interface.v index 63858b6c1e..2243e63de0 100644 --- a/vlib/v/checker/interface.v +++ b/vlib/v/checker/interface.v @@ -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 diff --git a/vlib/v/checker/tests/generic_interface_err.out b/vlib/v/checker/tests/generic_interface_err.out new file mode 100644 index 0000000000..c82c5ea6bf --- /dev/null +++ b/vlib/v/checker/tests/generic_interface_err.out @@ -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 + 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) + | ~~~~~~~~~~~~ diff --git a/vlib/v/checker/tests/generic_interface_err.vv b/vlib/v/checker/tests/generic_interface_err.vv new file mode 100644 index 0000000000..9817287914 --- /dev/null +++ b/vlib/v/checker/tests/generic_interface_err.vv @@ -0,0 +1,10 @@ +struct Struct { + value int +} + +interface Interface { + method() T +} + +s := Struct{7} +i := Interface(s) diff --git a/vlib/v/checker/tests/interface_generic_err.out b/vlib/v/checker/tests/interface_generic_err.out index f0a6c42c66..59a090909c 100644 --- a/vlib/v/checker/tests/interface_generic_err.out +++ b/vlib/v/checker/tests/interface_generic_err.out @@ -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) diff --git a/vlib/v/tests/generic_interface_infer_test.v b/vlib/v/tests/generic_interface_infer_test.v new file mode 100644 index 0000000000..44efe985d1 --- /dev/null +++ b/vlib/v/tests/generic_interface_infer_test.v @@ -0,0 +1,20 @@ +struct Struct { + value int + x T +} + +fn (s Struct) method() T { + return s.x + s.x +} + +interface Interface { + method() T +} + +fn test_infer_generic_interface() { + s := Struct{7, 5} + println(s) + i := Interface(s) + println(i) + assert i.method() == 10 +}