From 12ec0e9fe1eb34d41f01dc65fe5279d7a54c7e1c Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sat, 25 Feb 2023 09:44:41 -0300 Subject: [PATCH] cgen: support option pointer values - `?&Type` (#17397) --- vlib/v/checker/assign.v | 4 +- vlib/v/checker/checker.v | 4 ++ vlib/v/checker/tests/option_ptr_err.out | 6 +++ vlib/v/checker/tests/option_ptr_err.vv | 4 ++ vlib/v/gen/c/cgen.v | 7 +++- vlib/v/gen/c/str.v | 13 +++++- vlib/v/parser/expr.v | 9 ++++ vlib/v/tests/option_ptr_test.v | 56 +++++++++++++++++++++++++ 8 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 vlib/v/checker/tests/option_ptr_err.out create mode 100644 vlib/v/checker/tests/option_ptr_err.vv create mode 100644 vlib/v/tests/option_ptr_test.v diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index 8b559ef76c..575e7a9ee4 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -469,7 +469,9 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { rtype = rtype.deref() } right_name := c.table.type_to_str(rtype) - c.error('mismatched types `${left_name}` and `${right_name}`', node.pos) + if !(left_type.has_flag(.option) && right_type == ast.none_type) { + c.error('mismatched types `${left_name}` and `${right_name}`', node.pos) + } } } // Single side check diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index af49a7b6cf..5dcc6abffa 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3783,6 +3783,10 @@ fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type { } right_sym := c.table.final_sym(c.unwrap_generic(right_type)) if node.op == .mul { + if right_type.has_flag(.option) { + c.error('type `?${right_sym.name}` is an Option, it must be unwrapped first; use `*var?` to do it', + node.right.pos()) + } if right_type.is_ptr() { return right_type.deref() } diff --git a/vlib/v/checker/tests/option_ptr_err.out b/vlib/v/checker/tests/option_ptr_err.out new file mode 100644 index 0000000000..5096b8d5e1 --- /dev/null +++ b/vlib/v/checker/tests/option_ptr_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/option_ptr_err.vv:3:10: error: type `?int` is an Option, it must be unwrapped first; use `*var?` to do it + 1 | fn main() { + 2 | mut var := unsafe { ?&int(none) } + 3 | assert *var == 0 + | ~~~ + 4 | } diff --git a/vlib/v/checker/tests/option_ptr_err.vv b/vlib/v/checker/tests/option_ptr_err.vv new file mode 100644 index 0000000000..03e436350f --- /dev/null +++ b/vlib/v/checker/tests/option_ptr_err.vv @@ -0,0 +1,4 @@ +fn main() { + mut var := unsafe { ?&int(none) } + assert *var == 0 +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 4e76337abc..cec006540d 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4329,7 +4329,12 @@ fn (mut g Gen) gen_result_error(target_type ast.Type, expr ast.Expr) { fn (mut g Gen) gen_option_error(target_type ast.Type, expr ast.Expr) { styp := g.typ(g.unwrap_generic(target_type)) g.write('(${styp}){ .state=2, .err=') - g.expr(expr) + if target_type.has_flag(.option) && expr is ast.Ident + && (expr as ast.Ident).or_expr.kind == .propagate_option { + g.expr(ast.None{}) // option type unwrapping error + } else { + g.expr(expr) + } g.write(', .data={EMPTY_STRUCT_INITIALIZATION} }') } diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v index 7d617808e7..f3d5538445 100644 --- a/vlib/v/gen/c/str.v +++ b/vlib/v/gen/c/str.v @@ -111,12 +111,21 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { if is_ptr && !is_var_mut { ref_str := '&'.repeat(typ.nr_muls()) g.write('str_intp(1, _MOV((StrIntpData[]){{_SLIT("${ref_str}"), ${si_s_code} ,{.d_s = isnil(') - g.expr(expr) - g.write(') ? _SLIT("nil") : ') + if is_ptr && typ.has_flag(.option) { + g.write('*(${g.base_type(exp_typ)}*)&') + g.expr(expr) + g.write('.data') + g.write(') ? _SLIT("Option(&nil)") : ') + } else { + g.expr(expr) + g.write(') ? _SLIT("nil") : ') + } } g.write('${str_fn_name}(') if str_method_expects_ptr && !is_ptr { g.write('&') + } else if is_ptr && typ.has_flag(.option) { + g.write('*(${g.typ(typ.set_nr_muls(0))}*)&') } else if !str_method_expects_ptr && !is_shared && (is_ptr || is_var_mut) { g.write('*'.repeat(typ.nr_muls())) } diff --git a/vlib/v/parser/expr.v b/vlib/v/parser/expr.v index 9b53b40c95..2d0e88d569 100644 --- a/vlib/v/parser/expr.v +++ b/vlib/v/parser/expr.v @@ -52,6 +52,8 @@ pub fn (mut p Parser) check_expr(precedence int) !ast.Expr { && p.file_base in ['map.v', 'map_d_gcboehm_opt.v']) { p.error_with_pos("deprecated map syntax, use syntax like `{'age': 20}`", p.tok.pos()) + } else if p.tok.kind == .question && p.peek_tok.kind == .amp { + node = p.prefix_expr() } else { if p.inside_comptime_if && p.is_generic_name() && p.peek_tok.kind != .dot { // $if T is string {} @@ -660,6 +662,10 @@ fn (p &Parser) fileis(s string) bool { fn (mut p Parser) prefix_expr() ast.Expr { mut pos := p.tok.pos() + is_option := p.tok.kind == .question + if is_option { + p.next() + } op := p.tok.kind if op == .amp { p.is_amp = true @@ -678,6 +684,9 @@ fn (mut p Parser) prefix_expr() ast.Expr { if mut right is ast.CastExpr { // Handle &Type(x), as well as &&Type(x) etc: p.recast_as_pointer(mut right, pos) + if is_option { + right.typ = right.typ.set_flag(.option) + } return right } if mut right is ast.SelectorExpr { diff --git a/vlib/v/tests/option_ptr_test.v b/vlib/v/tests/option_ptr_test.v new file mode 100644 index 0000000000..1f80f08499 --- /dev/null +++ b/vlib/v/tests/option_ptr_test.v @@ -0,0 +1,56 @@ +fn test_simple_opt_ptr() { + val := 123 + mut var := unsafe { ?&int(&val) } + assert var? == unsafe { &int(&val) } +} + +fn test_simple_writing() { + val := 123 + mut var := unsafe { ?&int(&val) } + unsafe { + *var? = 321 + } + assert val == 321 +} + +fn test_simple_deref() ? { + val := 123 + mut var := unsafe { ?&int(&val) } + assert *var? == val + mut r := var? + unsafe { + *r = 321 + } + assert val == 321 +} + +fn f_ref(val ?&int) ? { + t := 123 + assert *val? == t +} + +fn test_simple_writing2() { + val := 123 + mut var := unsafe { ?&int(&val) } + unsafe { + *var? = 321 + } + x := *var? + assert val == x +} + +fn test_simple_fn() { + var := 123 + f_ref(&var) +} + +fn test_unset_opt_ptr() { + val := 123 + mut var := unsafe { ?&int(&val) } + unsafe { + *var? = 1 + } + assert var != none + var = none + assert var == none +}