From e9960339f92eca7c440bfc7d3be823982cf2b56c Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sun, 25 Jun 2023 03:07:14 -0300 Subject: [PATCH] cgen: fix generic resolver on non generic function (#18381) --- vlib/v/gen/c/cgen.v | 23 +++++++-- vlib/v/gen/c/utils.v | 19 ++++++++ vlib/v/tests/generic_selector_test.v | 70 ++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 vlib/v/tests/generic_selector_test.v diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 1c885e68d448d5..fc6d4e34aa777a 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1876,7 +1876,7 @@ fn (mut g Gen) expr_with_tmp_var(expr ast.Expr, expr_typ ast.Type, ret_typ ast.T } stmt_str := g.go_before_stmt(0).trim_space() - styp := g.base_type(ret_typ) + mut styp := g.base_type(ret_typ) g.empty_line = true if g.table.sym(expr_typ).kind == .none_ { @@ -1885,7 +1885,21 @@ fn (mut g Gen) expr_with_tmp_var(expr ast.Expr, expr_typ ast.Type, ret_typ ast.T g.writeln(';') } else { mut is_ptr_to_ptr_assign := false - g.writeln('${g.typ(ret_typ)} ${tmp_var};') + if ret_typ.has_flag(.generic) { + if expr is ast.SelectorExpr && g.cur_concrete_types.len == 0 { + // resolve generic struct on selectorExpr inside non-generic function + if expr.expr is ast.Ident && (expr.expr as ast.Ident).obj is ast.Var { + if ((expr.expr as ast.Ident).obj as ast.Var).expr is ast.StructInit { + g.cur_concrete_types << (g.table.sym((expr.expr as ast.Ident).obj.typ).info as ast.Struct).concrete_types + } + } + } + styp = g.base_type(g.unwrap_generic(ret_typ)) + ret_styp := g.typ(g.unwrap_generic(ret_typ)).replace('*', '_ptr') + g.writeln('${ret_styp} ${tmp_var};') + } else { + g.writeln('${g.typ(ret_typ)} ${tmp_var};') + } if ret_typ.has_flag(.option) { if expr_typ.has_flag(.option) && expr in [ast.StructInit, ast.ArrayInit, ast.MapInit] { if expr is ast.StructInit && (expr as ast.StructInit).init_fields.len > 0 { @@ -3693,8 +3707,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { return } } - field_is_opt := node.expr is ast.Ident && (node.expr as ast.Ident).is_auto_heap() - && (node.expr as ast.Ident).or_expr.kind != .absent && field_typ.has_flag(.option) + // var?.field_opt + field_is_opt := (node.expr is ast.Ident && (node.expr as ast.Ident).is_auto_heap() + && (node.expr as ast.Ident).or_expr.kind != .absent && field_typ.has_flag(.option)) if field_is_opt { g.write('((${g.base_type(field_typ)})') } diff --git a/vlib/v/gen/c/utils.v b/vlib/v/gen/c/utils.v index 9a56a623921895..0c4b156d76cc43 100644 --- a/vlib/v/gen/c/utils.v +++ b/vlib/v/gen/c/utils.v @@ -36,6 +36,25 @@ fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type { } } } + } else if typ.has_flag(.generic) && g.table.sym(typ).kind == .struct_ { + // resolve selector `a.foo` where `a` is struct[T] on non generic function + sym := g.table.sym(typ) + if sym.info is ast.Struct { + if sym.info.generic_types.len > 0 { + generic_names := sym.info.generic_types.map(g.table.sym(it).name) + if t_typ := mut_table.resolve_generic_to_concrete(typ, generic_names, + sym.info.concrete_types) + { + return t_typ + } + + if t_typ := mut_table.resolve_generic_to_concrete(typ, generic_names, + g.cur_concrete_types) + { + return t_typ + } + } + } } } return typ diff --git a/vlib/v/tests/generic_selector_test.v b/vlib/v/tests/generic_selector_test.v new file mode 100644 index 00000000000000..db31bddfb8d7d8 --- /dev/null +++ b/vlib/v/tests/generic_selector_test.v @@ -0,0 +1,70 @@ +pub struct List[T] { +mut: + head ?&Node[T] + size int +} + +pub fn (mut l List[T]) prepend(mut node Node[T]) { + if head := l.head { + node.next = head + l.head = &node + l.size = l.size + 1 + } else { + l.size = 1 + l.head = &node + } +} + +pub fn (mut l List[T]) append(mut node Node[T]) ?int { + if h := l.head { + _ := h + } else { + l.head = &node + l.size = 0 + return l.size + } + + mut curr_node := l.head + for { + if curr_node != none { + if next_node := curr_node?.next { + curr_node = next_node + } else { + curr_node?.next = &node + l.size = l.size + 1 + break + } + } + } + return l.size +} + +pub fn (mut l List[T]) find_last(node ?&Node[T]) ?&Node[T] { + if next := node?.next { + return l.find_last(next) + } else { + return node + } +} + +[heap] +pub struct Node[T] { +mut: + data T + next ?&Node[T] +} + +fn test_main() { + mut list := List[string]{} + list.prepend(mut Node{ data: 'zero' }) + list.prepend(mut Node{ data: 'first' }) + list.append(mut Node{ data: 'last' }) or { panic('unable to append linked list') } + list.append(mut Node{ data: 'very last' }) or { panic('unable to append linked list') } + + assert list.find_last(list.head)? == Node{ + data: 'very last' + } + assert list.head?.next?.next?.next? == Node{ + data: 'very last' + } +}