From 288ce180560f3a715e74c3fdf14fcfbbbe1c953a Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 23 Apr 2026 19:38:44 +0300 Subject: [PATCH] checker: fix Generic method unable to implement an interface with a different interface passed as argument (fixes #20670) --- vlib/v/checker/if.v | 3 +- vlib/v/checker/infix.v | 3 +- .../tests/interface_generic_implements_test.v | 41 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index 4ff196a6a..06e2bbf0c 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -699,7 +699,8 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope, co } if right_type != ast.no_type { - right_sym := c.table.sym(right_type) + resolved_right_type := c.unwrap_generic(right_type) + right_sym := c.table.final_sym(resolved_right_type) mut expr_type := c.unwrap_generic(node.left_type) left_sym := c.table.sym(expr_type) left_final_sym := c.table.final_sym(expr_type) diff --git a/vlib/v/checker/infix.v b/vlib/v/checker/infix.v index 6d887391d..861547128 100644 --- a/vlib/v/checker/infix.v +++ b/vlib/v/checker/infix.v @@ -965,7 +965,8 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { node.pos.line_nr + 1}') } } - typ_sym := c.table.sym(typ) + resolved_typ := c.unwrap_generic(typ) + typ_sym := c.table.final_sym(resolved_typ) op := node.op.str() if left_type.has_flag(.option) && !c.inside_sql { c.error('${node.left} is an Optional, it needs to be unwrapped first', diff --git a/vlib/v/tests/interface_generic_implements_test.v b/vlib/v/tests/interface_generic_implements_test.v index 2a4994fcc..f3c10e0a8 100644 --- a/vlib/v/tests/interface_generic_implements_test.v +++ b/vlib/v/tests/interface_generic_implements_test.v @@ -62,3 +62,44 @@ fn test_generic_interface_value_can_be_returned_as_interface_ref() { assert d.a == 200 assert e.a == 200 } + +interface FilterModel { + model string +} + +interface FilterCar { + name string +} + +struct FilterCarRecord { + name string + model string +} + +fn collect_matching[T](mut cars []FilterCar) []T { + mut matches := []T{} + for mut car in cars { + if mut car is T { + matches << car + } + } + return matches +} + +fn test_generic_interface_to_interface_smartcast() { + mut cars := []FilterCar{} + cars << FilterCarRecord{ + name: 'Roadster' + model: 'Tesla' + } + cars << FilterCarRecord{ + name: 'Corolla' + model: 'Toyota' + } + + models := collect_matching[FilterModel](mut cars) + + assert models.len == 2 + assert models[0].model == 'Tesla' + assert models[1].model == 'Toyota' +} -- 2.39.5