From 8d2a0ffe3784160389b491d5d7ee766d7802bb91 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sun, 4 Jun 2023 13:10:22 -0300 Subject: [PATCH] cgen: fix option handling with auto heap variable (#18336) --- vlib/v/ast/ast.v | 11 ++++ vlib/v/gen/c/assign.v | 18 ++++++- vlib/v/gen/c/cgen.v | 76 +++++++++++++++++++++++----- vlib/v/tests/option_auto_heap_test.v | 24 +++++++++ 4 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 vlib/v/tests/option_auto_heap_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index bd4c387b5b..3b0813ac17 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -962,6 +962,17 @@ pub mut: concrete_types []Type } +pub fn (i &Ident) is_auto_heap() bool { + match i.obj { + Var { + return i.obj.is_auto_heap + } + else { + return false + } + } +} + pub fn (i &Ident) is_mut() bool { match i.obj { Var { diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index eac7b14647..06a2e8cb3b 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -14,7 +14,12 @@ fn (mut g Gen) expr_with_opt_or_block(expr ast.Expr, expr_typ ast.Type, var_expr g.inside_opt_or_res = true g.expr_with_cast(expr, expr_typ, ret_typ) g.writeln(';') - g.writeln('if (${expr}.state != 0) {') + expr_var := if expr is ast.Ident && (expr as ast.Ident).is_auto_heap() { + '(*${expr.name})' + } else { + '${expr}' + } + g.writeln('if (${expr_var}.state != 0) { // assign') if expr is ast.Ident && (expr as ast.Ident).or_expr.kind == .propagate_option { g.writeln('\tpanic_option_not_set(_SLIT("none"));') } else { @@ -623,6 +628,9 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { g.inside_opt_or_res = old_inside_opt_or_res } g.inside_opt_or_res = true + if is_auto_heap && var_type.has_flag(.option) { + g.write('&') + } tmp_var := g.new_tmp_var() g.expr_with_tmp_var(val, val_type, var_type, tmp_var) } else if is_fixed_array_var { @@ -688,6 +696,14 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { } } } else { + // var = &auto_heap_var + old_is_auto_heap := g.is_option_auto_heap + g.is_option_auto_heap = val_type.has_flag(.option) && val is ast.PrefixExpr + && (val as ast.PrefixExpr).right is ast.Ident + && ((val as ast.PrefixExpr).right as ast.Ident).is_auto_heap() + defer { + g.is_option_auto_heap = old_is_auto_heap + } if var_type.has_flag(.option) || gen_or { g.expr_with_opt_or_block(val, val_type, left, var_type) } else if node.has_cross_var { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 11fb37834d..ec28db81a3 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -103,7 +103,8 @@ mut: is_json_fn bool // inside json.encode() is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)` is_fn_index_call bool - is_cc_msvc bool // g.pref.ccompiler == 'msvc' + is_cc_msvc bool // g.pref.ccompiler == 'msvc' + is_option_auto_heap bool vlines_path string // set to the proper path for generating #line directives options_pos_forward int // insertion point to forward options_forward []string // to forward @@ -3342,10 +3343,16 @@ fn (mut g Gen) expr(node_ ast.Expr) { } } } else { - if !(g.is_amp && node.right.is_auto_deref_var()) { + if g.is_option_auto_heap { + g.write('(${g.base_type(node.right_type)}*)') + } + if !g.is_option_auto_heap && !(g.is_amp && node.right.is_auto_deref_var()) { g.write(node.op.str()) } g.expr(node.right) + if g.is_option_auto_heap { + g.write('.data') + } } g.is_amp = false } @@ -3560,8 +3567,10 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } mut sum_type_deref_field := '' mut sum_type_dot := '.' + mut field_typ := ast.void_type if f := g.table.find_field_with_embeds(sym, node.field_name) { field_sym := g.table.sym(f.typ) + field_typ = f.typ if field_sym.kind in [.sum_type, .interface_] { if !prevent_sum_type_unwrapping_once { // check first if field is sum type because scope searching is expensive @@ -3660,6 +3669,11 @@ 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) + if field_is_opt { + g.write('((${g.base_type(field_typ)})') + } n_ptr := node.expr_type.nr_muls() - 1 if n_ptr > 0 { g.write('(') @@ -3669,6 +3683,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } else { g.expr(node.expr) } + if field_is_opt { + g.write(')') + } if is_opt_or_res { g.write('.data)') } @@ -3691,8 +3708,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } } alias_to_ptr := sym.info is ast.Alias && (sym.info as ast.Alias).parent_type.is_ptr() - if (node.expr_type.is_ptr() || sym.kind == .chan || alias_to_ptr) - && node.from_embed_types.len == 0 { + if field_is_opt + || ((node.expr_type.is_ptr() || sym.kind == .chan || alias_to_ptr) + && node.from_embed_types.len == 0) { g.write('->') } else { g.write('.') @@ -4157,27 +4175,37 @@ fn (mut g Gen) ident(node ast.Ident) { g.write('_const_') } } - mut is_auto_heap := false + mut is_auto_heap := node.is_auto_heap() + mut is_option := false if node.info is ast.IdentVar { if node.obj is ast.Var { if !g.is_assign_lhs && node.obj.ct_type_var !in [.generic_param, .no_comptime] { comptime_type := g.get_comptime_var_type(node) if comptime_type.has_flag(.option) { if (g.inside_opt_or_res || g.left_is_opt) && node.or_expr.kind == .absent { - g.write('${name}') + if !g.is_assign_lhs && is_auto_heap { + g.write('(*${name})') + } else { + g.write(name) + } } else { g.write('/*opt*/') styp := g.base_type(comptime_type) - g.write('(*(${styp}*)${name}.data)') + if is_auto_heap { + g.write('(*(${styp}*)${name}->data)') + } else { + g.write('(*(${styp}*)${name}.data)') + } } } else { - g.write('${name}') + g.write(name) } if node.or_expr.kind != .absent && !(g.inside_opt_or_res && g.inside_assign && !g.is_assign_lhs) { stmt_str := g.go_before_stmt(0).trim_space() g.empty_line = true - g.or_block(name, node.or_expr, comptime_type) + var_name := if !g.is_assign_lhs && is_auto_heap { '(*${name})' } else { name } + g.or_block(var_name, node.or_expr, comptime_type) g.writeln(stmt_str) } return @@ -4189,17 +4217,30 @@ fn (mut g Gen) ident(node ast.Ident) { // `println(x)` => `println(*(int*)x.data)` if node.info.is_option && !(g.is_assign_lhs && g.right_is_opt) { if (g.inside_opt_or_res || g.left_is_opt) && node.or_expr.kind == .absent { - g.write('${name}') + if !g.is_assign_lhs && is_auto_heap { + g.write('(*${name})') + } else { + g.write(name) + } } else { g.write('/*opt*/') styp := g.base_type(node.info.typ) - g.write('(*(${styp}*)${name}.data)') + if is_auto_heap { + g.write('(*(${styp}*)${name}->data)') + } else { + g.write('(*(${styp}*)${name}.data)') + } } if node.or_expr.kind != .absent && !(g.inside_opt_or_res && g.inside_assign && !g.is_assign_lhs) { stmt_str := g.go_before_stmt(0).trim_space() g.empty_line = true - g.or_block(name, node.or_expr, node.info.typ) + var_name := if !g.is_assign_lhs && is_auto_heap { + '(*${name})' + } else { + name + } + g.or_block(var_name, node.or_expr, node.info.typ) g.write(stmt_str) } return @@ -4208,6 +4249,7 @@ fn (mut g Gen) ident(node ast.Ident) { g.write('${name}.val') return } + is_option = node.info.is_option if node.obj is ast.Var { is_auto_heap = node.obj.is_auto_heap && (!g.is_assign_lhs || g.assign_op != .decl_assign) @@ -4280,6 +4322,16 @@ fn (mut g Gen) ident(node ast.Ident) { g.write(g.get_ternary_name(name)) if is_auto_heap { g.write('))') + if is_option { + g.write('.data') + } + } + if node.or_expr.kind != .absent && !(g.inside_opt_or_res && g.inside_assign && !g.is_assign_lhs) { + stmt_str := g.go_before_stmt(0).trim_space() + g.empty_line = true + var_opt := if is_auto_heap { '(*${name})' } else { name } + g.or_block(var_opt, node.or_expr, node.obj.typ) + g.write(stmt_str) } } diff --git a/vlib/v/tests/option_auto_heap_test.v b/vlib/v/tests/option_auto_heap_test.v new file mode 100644 index 0000000000..f918f66a79 --- /dev/null +++ b/vlib/v/tests/option_auto_heap_test.v @@ -0,0 +1,24 @@ +struct Teste { +pub mut: + teste ?&Teste +} + +fn test_main() { + mut a := Teste{} + a.teste = &a + dump(a) // circular + + assert a.teste? == &a + + mut t := ?Teste{} + w := dump(t) // Option(none) + assert w == none + + mut z := ?Teste{} + z = Teste{} + z?.teste = &z + dump(z) // // circular + + y := z? + assert y.teste? == &z? +}