all: basic implementation of result type (#14140)
parent
db185e6580
commit
08fd0ce0de
|
@ -118,6 +118,22 @@ fn opt_ok(data voidptr, mut option Option, size int) {
|
|||
}
|
||||
}
|
||||
|
||||
struct result {
|
||||
is_error bool
|
||||
err IError = none__
|
||||
// Data is trailing after err
|
||||
// and is not included in here but in the
|
||||
// derived Result_xxx types
|
||||
}
|
||||
|
||||
fn result_ok(data voidptr, mut res result, size int) {
|
||||
unsafe {
|
||||
*res = result{}
|
||||
// use err to get the end of ResultBase and then memcpy into it
|
||||
vmemcpy(&u8(&res.err) + sizeof(IError), data, size)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (_ none) str() string {
|
||||
return 'none'
|
||||
}
|
||||
|
|
|
@ -1487,7 +1487,8 @@ pub mut:
|
|||
pub enum OrKind {
|
||||
absent
|
||||
block
|
||||
propagate
|
||||
propagate_option
|
||||
propagate_result
|
||||
}
|
||||
|
||||
// `or { ... }`
|
||||
|
|
|
@ -210,7 +210,8 @@ pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
|
|||
}
|
||||
CallExpr {
|
||||
if sub_expr.args.len != 0 || sub_expr.concrete_types.len != 0
|
||||
|| sub_expr.or_block.kind == .propagate || sub_expr.or_block.stmts.len > 0 {
|
||||
|| sub_expr.or_block.kind == .propagate_option
|
||||
|| sub_expr.or_block.stmts.len > 0 {
|
||||
needs_braces = true
|
||||
} else if sub_expr.left is CallExpr {
|
||||
sub_expr = sub_expr.left
|
||||
|
@ -302,7 +303,7 @@ pub fn (x Expr) str() string {
|
|||
}
|
||||
CallExpr {
|
||||
sargs := args2str(x.args)
|
||||
propagate_suffix := if x.or_block.kind == .propagate { ' ?' } else { '' }
|
||||
propagate_suffix := if x.or_block.kind == .propagate_option { ' ?' } else { '' }
|
||||
if x.is_method {
|
||||
return '${x.left.str()}.${x.name}($sargs)$propagate_suffix'
|
||||
}
|
||||
|
|
|
@ -232,7 +232,7 @@ pub fn (t &Table) fn_type_signature(f &Fn) string {
|
|||
return sig
|
||||
}
|
||||
|
||||
// source_signature generates the signature of a function which looks like in the V source
|
||||
// fn_type_source_signature generates the signature of a function which looks like in the V source
|
||||
pub fn (t &Table) fn_type_source_signature(f &Fn) string {
|
||||
mut sig := '('
|
||||
for i, arg in f.params {
|
||||
|
@ -252,10 +252,14 @@ pub fn (t &Table) fn_type_source_signature(f &Fn) string {
|
|||
sig += ')'
|
||||
if f.return_type == ovoid_type {
|
||||
sig += ' ?'
|
||||
} else if f.return_type == rvoid_type {
|
||||
sig += ' !'
|
||||
} else if f.return_type != void_type {
|
||||
return_type_sym := t.sym(f.return_type)
|
||||
if f.return_type.has_flag(.optional) {
|
||||
sig += ' ?$return_type_sym.name'
|
||||
} else if f.return_type.has_flag(.result) {
|
||||
sig += ' !$return_type_sym.name'
|
||||
} else {
|
||||
sig += ' $return_type_sym.name'
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ pub mut:
|
|||
// max of 8
|
||||
pub enum TypeFlag {
|
||||
optional
|
||||
result
|
||||
variadic
|
||||
generic
|
||||
shared_f
|
||||
|
@ -472,6 +473,7 @@ pub const (
|
|||
pub const (
|
||||
void_type = new_type(void_type_idx)
|
||||
ovoid_type = new_type(void_type_idx).set_flag(.optional) // the return type of `fn () ?`
|
||||
rvoid_type = new_type(void_type_idx).set_flag(.result) // the return type of `fn () !`
|
||||
voidptr_type = new_type(voidptr_type_idx)
|
||||
byteptr_type = new_type(byteptr_type_idx)
|
||||
charptr_type = new_type(charptr_type_idx)
|
||||
|
@ -1227,6 +1229,9 @@ pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]
|
|||
if typ.has_flag(.optional) {
|
||||
return '?'
|
||||
}
|
||||
if typ.has_flag(.result) {
|
||||
return '!'
|
||||
}
|
||||
return 'void'
|
||||
}
|
||||
.thread {
|
||||
|
@ -1262,6 +1267,9 @@ pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]
|
|||
if typ.has_flag(.optional) {
|
||||
res = '?$res'
|
||||
}
|
||||
if typ.has_flag(.result) {
|
||||
res = '!$res'
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
|
|
|
@ -89,12 +89,15 @@ pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
|||
if expected == ast.charptr_type && got == ast.char_type.ref() {
|
||||
return true
|
||||
}
|
||||
if expected.has_flag(.optional) {
|
||||
if expected.has_flag(.optional) || expected.has_flag(.result) {
|
||||
sym := c.table.sym(got)
|
||||
if sym.idx == ast.error_type_idx || got in [ast.none_type, ast.error_type] {
|
||||
if ((sym.idx == ast.error_type_idx || got in [ast.none_type, ast.error_type])
|
||||
&& expected.has_flag(.optional))
|
||||
|| ((sym.idx == ast.error_type_idx || got == ast.error_type)
|
||||
&& expected.has_flag(.result)) {
|
||||
// IErorr
|
||||
return true
|
||||
} else if !c.check_basic(got, expected.clear_flag(.optional)) {
|
||||
} else if !c.check_basic(got, expected.clear_flag(.optional).clear_flag(.result)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1488,39 +1488,61 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to
|
|||
// return the actual type of the expression, once the optional is handled
|
||||
pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Type {
|
||||
if expr is ast.CallExpr {
|
||||
if expr.return_type.has_flag(.optional) {
|
||||
if expr.return_type.has_flag(.optional) || expr.return_type.has_flag(.result) {
|
||||
return_modifier_kind := if expr.return_type.has_flag(.optional) {
|
||||
'an option'
|
||||
} else {
|
||||
'a result'
|
||||
}
|
||||
return_modifier := if expr.return_type.has_flag(.optional) { '?' } else { '!' }
|
||||
if expr.or_block.kind == .absent {
|
||||
if c.inside_defer {
|
||||
c.error('${expr.name}() returns an option, so it should have an `or {}` block at the end',
|
||||
c.error('${expr.name}() returns $return_modifier_kind, so it should have an `or {}` block at the end',
|
||||
expr.pos)
|
||||
} else {
|
||||
c.error('${expr.name}() returns an option, so it should have either an `or {}` block, or `?` at the end',
|
||||
c.error('${expr.name}() returns $return_modifier_kind, so it should have either an `or {}` block, or `$return_modifier` at the end',
|
||||
expr.pos)
|
||||
}
|
||||
} else {
|
||||
c.check_or_expr(expr.or_block, ret_type, expr.return_type.clear_flag(.optional))
|
||||
c.check_or_expr(expr.or_block, ret_type, expr.return_type)
|
||||
}
|
||||
return ret_type.clear_flag(.optional)
|
||||
} else if expr.or_block.kind == .block {
|
||||
c.error('unexpected `or` block, the function `$expr.name` does not return an optional',
|
||||
c.error('unexpected `or` block, the function `$expr.name` does neither return an optional nor a result',
|
||||
expr.or_block.pos)
|
||||
} else if expr.or_block.kind == .propagate {
|
||||
} else if expr.or_block.kind == .propagate_option {
|
||||
c.error('unexpected `?`, the function `$expr.name` does not return an optional',
|
||||
expr.or_block.pos)
|
||||
}
|
||||
} else if expr is ast.IndexExpr {
|
||||
if expr.or_expr.kind != .absent {
|
||||
c.check_or_expr(expr.or_expr, ret_type, ret_type)
|
||||
c.check_or_expr(expr.or_expr, ret_type, ret_type.set_flag(.optional))
|
||||
}
|
||||
}
|
||||
return ret_type
|
||||
}
|
||||
|
||||
pub fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_return_type ast.Type) {
|
||||
if node.kind == .propagate {
|
||||
if node.kind == .propagate_option {
|
||||
if !c.table.cur_fn.return_type.has_flag(.optional) && c.table.cur_fn.name != 'main.main'
|
||||
&& !c.inside_const {
|
||||
c.error('to propagate the optional call, `$c.table.cur_fn.name` must return an optional',
|
||||
c.error('to propagate the call, `$c.table.cur_fn.name` must return an optional type',
|
||||
node.pos)
|
||||
}
|
||||
if !expr_return_type.has_flag(.optional) {
|
||||
c.error('to propagate an option, the call must also return an optional type',
|
||||
node.pos)
|
||||
}
|
||||
return
|
||||
}
|
||||
if node.kind == .propagate_result {
|
||||
if !c.table.cur_fn.return_type.has_flag(.result) && c.table.cur_fn.name != 'main.main'
|
||||
&& !c.inside_const {
|
||||
c.error('to propagate the call, `$c.table.cur_fn.name` must return an result type',
|
||||
node.pos)
|
||||
}
|
||||
if !expr_return_type.has_flag(.result) {
|
||||
c.error('to propagate a result, the call must also return a result type',
|
||||
node.pos)
|
||||
}
|
||||
return
|
||||
|
@ -1535,7 +1557,7 @@ pub fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_re
|
|||
return
|
||||
}
|
||||
last_stmt := node.stmts[stmts_len - 1]
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type.clear_flag(.optional).clear_flag(.result))
|
||||
}
|
||||
|
||||
fn (mut c Checker) check_or_last_stmt(stmt ast.Stmt, ret_type ast.Type, expr_return_type ast.Type) {
|
||||
|
@ -2600,18 +2622,23 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
|||
}
|
||||
ast.CallExpr {
|
||||
mut ret_type := c.call_expr(mut node)
|
||||
if !ret_type.has_flag(.optional) {
|
||||
if !ret_type.has_flag(.optional) && !ret_type.has_flag(.result) {
|
||||
if node.or_block.kind == .block {
|
||||
c.error('unexpected `or` block, the function `$node.name` does not return an optional',
|
||||
c.error('unexpected `or` block, the function `$node.name` does neither return an optional nor a result',
|
||||
node.or_block.pos)
|
||||
} else if node.or_block.kind == .propagate {
|
||||
c.error('unexpected `?`, the function `$node.name` does not return an optional',
|
||||
} else if node.or_block.kind == .propagate_option {
|
||||
c.error('unexpected `?`, the function `$node.name` does neither return an optional nor a result',
|
||||
node.or_block.pos)
|
||||
}
|
||||
}
|
||||
if ret_type.has_flag(.optional) && node.or_block.kind != .absent {
|
||||
if node.or_block.kind != .absent {
|
||||
if ret_type.has_flag(.optional) {
|
||||
ret_type = ret_type.clear_flag(.optional)
|
||||
}
|
||||
if ret_type.has_flag(.result) {
|
||||
ret_type = ret_type.clear_flag(.result)
|
||||
}
|
||||
}
|
||||
return ret_type
|
||||
}
|
||||
ast.ChanInit {
|
||||
|
|
|
@ -310,6 +310,16 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// same for result `fn (...) ! { ... }`
|
||||
if node.return_type != ast.void_type && node.return_type.has_flag(.result)
|
||||
&& (node.stmts.len == 0 || node.stmts.last() !is ast.Return) {
|
||||
sym := c.table.sym(node.return_type)
|
||||
if sym.kind == .void {
|
||||
node.stmts << ast.Return{
|
||||
pos: node.pos
|
||||
}
|
||||
}
|
||||
}
|
||||
c.fn_scope = node.scope
|
||||
c.stmts(node.stmts)
|
||||
node_has_top_return := has_top_return(node.stmts)
|
||||
|
@ -411,7 +421,7 @@ pub fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
|
|||
c.expected_or_type = node.return_type.clear_flag(.optional)
|
||||
c.stmts_ending_with_expression(node.or_block.stmts)
|
||||
c.expected_or_type = ast.void_type
|
||||
if node.or_block.kind == .propagate && !c.table.cur_fn.return_type.has_flag(.optional)
|
||||
if node.or_block.kind == .propagate_option && !c.table.cur_fn.return_type.has_flag(.optional)
|
||||
&& !c.inside_const {
|
||||
if !c.table.cur_fn.is_main {
|
||||
c.error('to propagate the optional call, `$c.table.cur_fn.name` must return an optional',
|
||||
|
|
|
@ -158,7 +158,7 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) {
|
|||
if exp_is_optional && node.exprs.len > 0 {
|
||||
expr0 := node.exprs[0]
|
||||
if expr0 is ast.CallExpr {
|
||||
if expr0.or_block.kind == .propagate && node.exprs.len == 1 {
|
||||
if expr0.or_block.kind == .propagate_option && node.exprs.len == 1 {
|
||||
c.error('`?` is not needed, use `return ${expr0.name}()`', expr0.pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import v.ast
|
|||
import v.token
|
||||
|
||||
pub fn (mut c Checker) get_default_fmt(ftyp ast.Type, typ ast.Type) u8 {
|
||||
if ftyp.has_flag(.optional) {
|
||||
if ftyp.has_flag(.optional) || ftyp.has_flag(.result) {
|
||||
return `s`
|
||||
} else if typ.is_float() {
|
||||
return `g`
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
vlib/v/checker/tests/fn_return_or_err.vv:6:17: error: unexpected `or` block, the function `pop` does not return an optional
|
||||
vlib/v/checker/tests/fn_return_or_err.vv:6:17: error: unexpected `or` block, the function `pop` does neither return an optional nor a result
|
||||
4 |
|
||||
5 | pub fn next(mut v []Typ) Typ {
|
||||
6 | return v.pop() or { Typ{} }
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
vlib/v/checker/tests/go_wait_or.vv:11:17: error: unexpected `?`, the function `wait` does not return an optional
|
||||
vlib/v/checker/tests/go_wait_or.vv:11:17: error: unexpected `?`, the function `wait` does neither return an optional nor a result
|
||||
9 | go d(1)
|
||||
10 | ]
|
||||
11 | r := tg.wait() ?
|
||||
| ^
|
||||
12 | println(r)
|
||||
13 | s := tg[0].wait() or { panic('problem') }
|
||||
vlib/v/checker/tests/go_wait_or.vv:13:20: error: unexpected `or` block, the function `wait` does not return an optional
|
||||
vlib/v/checker/tests/go_wait_or.vv:13:20: error: unexpected `or` block, the function `wait` does neither return an optional nor a result
|
||||
11 | r := tg.wait() ?
|
||||
12 | println(r)
|
||||
13 | s := tg[0].wait() or { panic('problem') }
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
14 | println(s)
|
||||
15 | tg2 := [
|
||||
vlib/v/checker/tests/go_wait_or.vv:19:13: error: unexpected `or` block, the function `wait` does not return an optional
|
||||
vlib/v/checker/tests/go_wait_or.vv:19:13: error: unexpected `or` block, the function `wait` does neither return an optional nor a result
|
||||
17 | go e(1)
|
||||
18 | ]
|
||||
19 | tg2.wait() or { panic('problem') }
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
20 | tg2[0].wait() ?
|
||||
21 | tg3 := [
|
||||
vlib/v/checker/tests/go_wait_or.vv:20:16: error: unexpected `?`, the function `wait` does not return an optional
|
||||
vlib/v/checker/tests/go_wait_or.vv:20:16: error: unexpected `?`, the function `wait` does neither return an optional nor a result
|
||||
18 | ]
|
||||
19 | tg2.wait() or { panic('problem') }
|
||||
20 | tg2[0].wait() ?
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/propagate_option_with_result_err.vv:6:8: error: to propagate an option, the call must also return an optional type
|
||||
4 |
|
||||
5 | fn bar() ?string {
|
||||
6 | foo() ?
|
||||
| ^
|
||||
7 | return ''
|
||||
8 | }
|
|
@ -0,0 +1,10 @@
|
|||
fn foo() !string {
|
||||
return ''
|
||||
}
|
||||
|
||||
fn bar() ?string {
|
||||
foo() ?
|
||||
return ''
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,7 @@
|
|||
vlib/v/checker/tests/propagate_result_with_option.vv:6:8: error: to propagate a result, the call must also return a result type
|
||||
4 |
|
||||
5 | fn bar() !string {
|
||||
6 | foo() !
|
||||
| ^
|
||||
7 | return ''
|
||||
8 | }
|
|
@ -0,0 +1,10 @@
|
|||
fn foo() ?string {
|
||||
return ''
|
||||
}
|
||||
|
||||
fn bar() !string {
|
||||
foo() !
|
||||
return ''
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -1,4 +1,4 @@
|
|||
vlib/v/checker/tests/unexpected_or.vv:6:17: error: unexpected `or` block, the function `ret_zero` does not return an optional
|
||||
vlib/v/checker/tests/unexpected_or.vv:6:17: error: unexpected `or` block, the function `ret_zero` does neither return an optional nor a result
|
||||
4 |
|
||||
5 | fn main() {
|
||||
6 | _ = ret_zero() or { 1 }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
vlib/v/checker/tests/unexpected_or_propagate.vv:6:17: error: unexpected `?`, the function `ret_zero` does not return an optional
|
||||
vlib/v/checker/tests/unexpected_or_propagate.vv:6:17: error: unexpected `?`, the function `ret_zero` does neither return an optional nor a result
|
||||
4 |
|
||||
5 | fn opt_fn() ?int {
|
||||
6 | a := ret_zero()?
|
||||
|
|
|
@ -1335,6 +1335,8 @@ pub fn (mut f Fmt) fn_type_decl(node ast.FnTypeDecl) {
|
|||
f.write(' $ret_str')
|
||||
} else if fn_info.return_type.has_flag(.optional) {
|
||||
f.write(' ?')
|
||||
} else if fn_info.return_type.has_flag(.result) {
|
||||
f.write(' !')
|
||||
}
|
||||
|
||||
f.comments(node.comments, has_nl: false)
|
||||
|
@ -2315,9 +2317,12 @@ pub fn (mut f Fmt) or_expr(node ast.OrExpr) {
|
|||
f.stmts(node.stmts)
|
||||
f.write('}')
|
||||
}
|
||||
.propagate {
|
||||
.propagate_option {
|
||||
f.write(' ?')
|
||||
}
|
||||
.propagate_result {
|
||||
f.write(' !')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
fn foo() !int {
|
||||
return 1
|
||||
}
|
|
@ -159,7 +159,7 @@ fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) {
|
|||
if expr is ast.CTempVar {
|
||||
if expr.orig is ast.CallExpr {
|
||||
should_clone = false
|
||||
if expr.orig.or_block.kind == .propagate {
|
||||
if expr.orig.or_block.kind == .propagate_option {
|
||||
should_clone = true
|
||||
}
|
||||
if expr.orig.is_method && expr.orig.args.len == 0
|
||||
|
|
|
@ -169,7 +169,8 @@ fn (mut g Gen) final_gen_str(typ StrType) {
|
|||
}
|
||||
g.generated_str_fns << typ
|
||||
sym := g.table.sym(typ.typ)
|
||||
if sym.has_method_with_generic_parent('str') && !typ.typ.has_flag(.optional) {
|
||||
if sym.has_method_with_generic_parent('str') && !(typ.typ.has_flag(.optional)
|
||||
|| typ.typ.has_flag(.result)) {
|
||||
return
|
||||
}
|
||||
styp := typ.styp
|
||||
|
@ -178,6 +179,10 @@ fn (mut g Gen) final_gen_str(typ StrType) {
|
|||
g.gen_str_for_option(typ.typ, styp, str_fn_name)
|
||||
return
|
||||
}
|
||||
if typ.typ.has_flag(.result) {
|
||||
g.gen_str_for_result(typ.typ, styp, str_fn_name)
|
||||
return
|
||||
}
|
||||
match sym.info {
|
||||
ast.Alias {
|
||||
if sym.info.is_import {
|
||||
|
@ -258,6 +263,39 @@ fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string)
|
|||
g.auto_str_funcs.writeln('}')
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_str_for_result(typ ast.Type, styp string, str_fn_name string) {
|
||||
$if trace_autostr ? {
|
||||
eprintln('> gen_str_for_result: $typ.debug() | $styp | $str_fn_name')
|
||||
}
|
||||
parent_type := typ.clear_flag(.result)
|
||||
sym := g.table.sym(parent_type)
|
||||
sym_has_str_method, _, _ := sym.str_method_info()
|
||||
parent_str_fn_name := g.get_str_fn(parent_type)
|
||||
|
||||
g.definitions.writeln('string ${str_fn_name}($styp it); // auto')
|
||||
g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }')
|
||||
g.definitions.writeln('string indent_${str_fn_name}($styp it, int indent_count); // auto')
|
||||
g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp it, int indent_count) {')
|
||||
g.auto_str_funcs.writeln('\tstring res;')
|
||||
g.auto_str_funcs.writeln('\tif (!it.is_error) {')
|
||||
if sym.kind == .string {
|
||||
tmp_res := '${parent_str_fn_name}(*($sym.cname*)it.data)'
|
||||
g.auto_str_funcs.writeln('\t\tres = ${str_intp_sq(tmp_res)};')
|
||||
} else if should_use_indent_func(sym.kind) && !sym_has_str_method {
|
||||
g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(*($sym.cname*)it.data, indent_count);')
|
||||
} else {
|
||||
g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(*($sym.cname*)it.data);')
|
||||
}
|
||||
g.auto_str_funcs.writeln('\t} else {')
|
||||
|
||||
tmp_str := str_intp_sub('error: %%', 'IError_str(it.err)')
|
||||
g.auto_str_funcs.writeln('\t\tres = $tmp_str;')
|
||||
g.auto_str_funcs.writeln('\t}')
|
||||
|
||||
g.auto_str_funcs.writeln('\treturn ${str_intp_sub('result(%%)', 'res')};')
|
||||
g.auto_str_funcs.writeln('}')
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_str_for_alias(info ast.Alias, styp string, str_fn_name string) {
|
||||
parent_str_fn_name := g.get_str_fn(info.parent_type)
|
||||
$if trace_autostr ? {
|
||||
|
@ -506,6 +544,8 @@ fn (mut g Gen) fn_decl_str(info ast.FnType) string {
|
|||
fn_str += ')'
|
||||
if info.func.return_type == ast.ovoid_type {
|
||||
fn_str += ' ?'
|
||||
} else if info.func.return_type == ast.rvoid_type {
|
||||
fn_str += ' !'
|
||||
} else if info.func.return_type != ast.void_type {
|
||||
x := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.func.return_type)))
|
||||
if info.func.return_type.has_flag(.optional) {
|
||||
|
|
|
@ -73,7 +73,8 @@ mut:
|
|||
embedded_data strings.Builder // data to embed in the executable/binary
|
||||
shared_types strings.Builder // shared/lock types
|
||||
shared_functions strings.Builder // shared constructors
|
||||
options strings.Builder // `Option_xxxx` types
|
||||
options strings.Builder // `option_xxxx` types
|
||||
out_results strings.Builder // `result_xxxx` types
|
||||
json_forward_decls strings.Builder // json type forward decls
|
||||
sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
|
||||
file &ast.File
|
||||
|
@ -100,6 +101,7 @@ mut:
|
|||
is_cc_msvc bool // g.pref.ccompiler == 'msvc'
|
||||
vlines_path string // set to the proper path for generating #line directives
|
||||
optionals map[string]string // to avoid duplicates
|
||||
results map[string]string // to avoid duplicates
|
||||
done_optionals shared []string // to avoid duplicates
|
||||
chan_pop_optionals map[string]string // types for `x := <-ch or {...}`
|
||||
chan_push_optionals map[string]string // types for `ch <- x or {...}`
|
||||
|
@ -246,6 +248,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
|||
pcs_declarations: strings.new_builder(100)
|
||||
embedded_data: strings.new_builder(1000)
|
||||
options: strings.new_builder(100)
|
||||
out_results: strings.new_builder(100)
|
||||
shared_types: strings.new_builder(100)
|
||||
shared_functions: strings.new_builder(100)
|
||||
json_forward_decls: strings.new_builder(100)
|
||||
|
@ -319,6 +322,9 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
|||
for k, v in g.optionals {
|
||||
global_g.optionals[k] = v
|
||||
}
|
||||
for k, v in g.results {
|
||||
global_g.results[k] = v
|
||||
}
|
||||
for k, v in g.as_cast_type_names {
|
||||
global_g.as_cast_type_names[k] = v
|
||||
}
|
||||
|
@ -373,6 +379,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
|||
|
||||
global_g.gen_jsons()
|
||||
global_g.write_optionals()
|
||||
global_g.write_results()
|
||||
global_g.dump_expr_definitions() // this uses global_g.get_str_fn, so it has to go before the below for loop
|
||||
for i := 0; i < global_g.str_types.len; i++ {
|
||||
global_g.final_gen_str(global_g.str_types[i])
|
||||
|
@ -441,6 +448,8 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
|||
b.write_string(g.shared_types.str())
|
||||
b.writeln('\n// V Option_xxx definitions:')
|
||||
b.write_string(g.options.str())
|
||||
b.writeln('\n// V result_xxx definitions:')
|
||||
b.write_string(g.out_results.str())
|
||||
b.writeln('\n// V json forward decls:')
|
||||
b.write_string(g.json_forward_decls.str())
|
||||
b.writeln('\n// V definitions:')
|
||||
|
@ -535,6 +544,7 @@ fn cgen_process_one_file_cb(p &pool.PoolProcessor, idx int, wid int) &Gen {
|
|||
hotcode_definitions: strings.new_builder(100)
|
||||
embedded_data: strings.new_builder(1000)
|
||||
options: strings.new_builder(100)
|
||||
out_results: strings.new_builder(100)
|
||||
shared_types: strings.new_builder(100)
|
||||
shared_functions: strings.new_builder(100)
|
||||
channel_definitions: strings.new_builder(100)
|
||||
|
@ -595,6 +605,7 @@ pub fn (mut g Gen) free_builders() {
|
|||
g.shared_functions.free()
|
||||
g.channel_definitions.free()
|
||||
g.options.free()
|
||||
g.out_results.free()
|
||||
g.json_forward_decls.free()
|
||||
g.enum_typedefs.free()
|
||||
g.sql_buf.free()
|
||||
|
@ -884,6 +895,8 @@ fn (mut g Gen) typ(t ast.Type) string {
|
|||
if t.has_flag(.optional) {
|
||||
// Register an optional if it's not registered yet
|
||||
return g.register_optional(t)
|
||||
} else if t.has_flag(.result) {
|
||||
return g.register_result(t)
|
||||
} else {
|
||||
return g.base_type(t)
|
||||
}
|
||||
|
@ -965,6 +978,15 @@ fn (mut g Gen) optional_type_name(t ast.Type) (string, string) {
|
|||
return styp, base
|
||||
}
|
||||
|
||||
fn (mut g Gen) result_type_name(t ast.Type) (string, string) {
|
||||
base := g.base_type(t)
|
||||
mut styp := 'result_$base'
|
||||
if t.is_ptr() {
|
||||
styp = styp.replace('*', '_ptr')
|
||||
}
|
||||
return styp, base
|
||||
}
|
||||
|
||||
fn (g Gen) optional_type_text(styp string, base string) string {
|
||||
// replace void with something else
|
||||
size := if base == 'void' {
|
||||
|
@ -982,12 +1004,35 @@ fn (g Gen) optional_type_text(styp string, base string) string {
|
|||
return ret
|
||||
}
|
||||
|
||||
fn (g Gen) result_type_text(styp string, base string) string {
|
||||
// replace void with something else
|
||||
size := if base == 'void' {
|
||||
'u8'
|
||||
} else if base.starts_with('anon_fn') {
|
||||
'void*'
|
||||
} else {
|
||||
base
|
||||
}
|
||||
ret := 'struct $styp {
|
||||
bool is_error;
|
||||
IError err;
|
||||
byte data[sizeof($size) > 0 ? sizeof($size) : 1];
|
||||
}'
|
||||
return ret
|
||||
}
|
||||
|
||||
fn (mut g Gen) register_optional(t ast.Type) string {
|
||||
styp, base := g.optional_type_name(t)
|
||||
g.optionals[base] = styp
|
||||
return styp
|
||||
}
|
||||
|
||||
fn (mut g Gen) register_result(t ast.Type) string {
|
||||
styp, base := g.result_type_name(t)
|
||||
g.results[base] = styp
|
||||
return styp
|
||||
}
|
||||
|
||||
fn (mut g Gen) write_optionals() {
|
||||
mut done := []string{}
|
||||
rlock g.done_optionals {
|
||||
|
@ -1003,6 +1048,18 @@ fn (mut g Gen) write_optionals() {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) write_results() {
|
||||
mut done := []string{}
|
||||
for base, styp in g.results {
|
||||
if base in done {
|
||||
continue
|
||||
}
|
||||
done << base
|
||||
g.typedefs.writeln('typedef struct $styp $styp;')
|
||||
g.out_results.write_string(g.result_type_text(styp, base) + ';\n\n')
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string {
|
||||
g.shareds[t.idx()] = base
|
||||
return '__shared__$base'
|
||||
|
@ -1881,6 +1938,10 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
|||
// Register an optional if it's not registered yet
|
||||
g.register_optional(method.return_type)
|
||||
}
|
||||
if method.return_type.has_flag(.result) {
|
||||
// Register a result if it's not registered yet
|
||||
g.register_result(method.return_type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3893,6 +3954,14 @@ fn (g &Gen) expr_is_multi_return_call(expr ast.Expr) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_result_error(target_type ast.Type, expr ast.Expr) {
|
||||
styp := g.typ(target_type)
|
||||
g.write('($styp){ .is_error=true, .err=')
|
||||
g.expr(expr)
|
||||
g.write(', .data={EMPTY_STRUCT_INITIALIZATION} }')
|
||||
}
|
||||
|
||||
// NB: remove this when optional has no errors anymore
|
||||
fn (mut g Gen) gen_optional_error(target_type ast.Type, expr ast.Expr) {
|
||||
styp := g.typ(target_type)
|
||||
g.write('($styp){ .state=2, .err=')
|
||||
|
@ -3921,10 +3990,11 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||
sym := g.table.sym(g.fn_decl.return_type)
|
||||
fn_return_is_multi := sym.kind == .multi_return
|
||||
fn_return_is_optional := g.fn_decl.return_type.has_flag(.optional)
|
||||
fn_return_is_result := g.fn_decl.return_type.has_flag(.result)
|
||||
mut has_semicolon := false
|
||||
if node.exprs.len == 0 {
|
||||
g.write_defer_stmts_when_needed()
|
||||
if fn_return_is_optional {
|
||||
if fn_return_is_optional || fn_return_is_result {
|
||||
styp := g.typ(g.fn_decl.return_type)
|
||||
g.writeln('return ($styp){0};')
|
||||
} else {
|
||||
|
@ -3969,6 +4039,34 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||
return
|
||||
}
|
||||
}
|
||||
// handle promoting error/function returning 'result'
|
||||
if fn_return_is_result {
|
||||
ftyp := g.typ(node.types[0])
|
||||
mut is_regular_result := ftyp == 'result'
|
||||
if is_regular_result || node.types[0] == ast.error_type_idx {
|
||||
if !isnil(g.fn_decl) && g.fn_decl.is_test {
|
||||
test_error_var := g.new_tmp_var()
|
||||
g.write('$ret_typ $test_error_var = ')
|
||||
g.gen_result_error(g.fn_decl.return_type, node.exprs[0])
|
||||
g.writeln(';')
|
||||
g.write_defer_stmts_when_needed()
|
||||
g.gen_failing_return_error_for_test_fn(node, test_error_var)
|
||||
return
|
||||
}
|
||||
if use_tmp_var {
|
||||
g.write('$ret_typ $tmpvar = ')
|
||||
} else {
|
||||
g.write('return ')
|
||||
}
|
||||
g.gen_result_error(g.fn_decl.return_type, node.exprs[0])
|
||||
g.writeln(';')
|
||||
if use_tmp_var {
|
||||
g.write_defer_stmts_when_needed()
|
||||
g.writeln('return $tmpvar;')
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
// regular cases
|
||||
if fn_return_is_multi && node.exprs.len > 0 && !g.expr_is_multi_return_call(node.exprs[0]) {
|
||||
if node.exprs.len == 1 && (node.exprs[0] is ast.IfExpr || node.exprs[0] is ast.MatchExpr) {
|
||||
|
@ -3983,7 +4081,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||
typ_sym := g.table.sym(g.fn_decl.return_type)
|
||||
mr_info := typ_sym.info as ast.MultiReturn
|
||||
mut styp := ''
|
||||
if fn_return_is_optional {
|
||||
if fn_return_is_optional || fn_return_is_result {
|
||||
g.writeln('$ret_typ $tmpvar;')
|
||||
styp = g.base_type(g.fn_decl.return_type)
|
||||
g.write('opt_ok(&($styp/*X*/[]) { ')
|
||||
|
@ -4054,7 +4152,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||
}
|
||||
}
|
||||
g.write('}')
|
||||
if fn_return_is_optional {
|
||||
if fn_return_is_optional || fn_return_is_result {
|
||||
g.writeln(' }, (Option*)(&$tmpvar), sizeof($styp));')
|
||||
g.write_defer_stmts_when_needed()
|
||||
g.write('return $tmpvar')
|
||||
|
@ -4063,7 +4161,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||
if multi_unpack.len > 0 {
|
||||
g.insert_before_stmt(multi_unpack)
|
||||
}
|
||||
if use_tmp_var && !fn_return_is_optional {
|
||||
if use_tmp_var && !fn_return_is_optional && !fn_return_is_result {
|
||||
if !has_semicolon {
|
||||
g.writeln(';')
|
||||
}
|
||||
|
@ -4109,6 +4207,35 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
|||
g.writeln('return $tmpvar;')
|
||||
return
|
||||
}
|
||||
expr_type_is_result := match expr0 {
|
||||
ast.CallExpr {
|
||||
expr0.return_type.has_flag(.result) && expr0.or_block.kind == .absent
|
||||
}
|
||||
else {
|
||||
node.types[0].has_flag(.result)
|
||||
}
|
||||
}
|
||||
if fn_return_is_result && !expr_type_is_result && return_sym.name != 'result' {
|
||||
styp := g.base_type(g.fn_decl.return_type)
|
||||
g.writeln('$ret_typ $tmpvar;')
|
||||
g.write('result_ok(&($styp[]) { ')
|
||||
if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() {
|
||||
if !(node.exprs[0] is ast.Ident && !g.is_amp) {
|
||||
g.write('*')
|
||||
}
|
||||
}
|
||||
for i, expr in node.exprs {
|
||||
g.expr_with_cast(expr, node.types[i], g.fn_decl.return_type.clear_flag(.result))
|
||||
if i < node.exprs.len - 1 {
|
||||
g.write(', ')
|
||||
}
|
||||
}
|
||||
g.writeln(' }, (result*)(&$tmpvar), sizeof($styp));')
|
||||
g.write_defer_stmts_when_needed()
|
||||
g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
|
||||
g.writeln('return $tmpvar;')
|
||||
return
|
||||
}
|
||||
// autofree before `return`
|
||||
// set free_parent_scopes to true, since all variables defined in parent
|
||||
// scopes need to be freed before the return
|
||||
|
@ -4596,7 +4723,7 @@ fn (mut g Gen) write_init_function() {
|
|||
}
|
||||
|
||||
const (
|
||||
builtins = ['string', 'array', 'DenseArray', 'map', 'Error', 'IError', 'Option']
|
||||
builtins = ['string', 'array', 'DenseArray', 'map', 'Error', 'IError', 'Option', 'result']
|
||||
)
|
||||
|
||||
fn (mut g Gen) write_builtin_types() {
|
||||
|
@ -4933,8 +5060,12 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty
|
|||
if return_type != 0 && g.table.sym(return_type).kind == .function {
|
||||
mr_styp = 'voidptr'
|
||||
}
|
||||
if return_type.has_flag(.result) {
|
||||
g.writeln('if (${cvar_name}.is_error) { /*or block*/ ')
|
||||
} else {
|
||||
g.writeln('if (${cvar_name}.state != 0) { /*or block*/ ')
|
||||
}
|
||||
}
|
||||
if or_block.kind == .block {
|
||||
g.or_expr_return_type = return_type.clear_flag(.optional)
|
||||
if g.inside_or_block {
|
||||
|
@ -4975,7 +5106,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty
|
|||
}
|
||||
}
|
||||
g.or_expr_return_type = ast.void_type
|
||||
} else if or_block.kind == .propagate {
|
||||
} else if or_block.kind == .propagate_option {
|
||||
if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.is_main) {
|
||||
// In main(), an `opt()?` call is sugar for `opt() or { panic(err) }`
|
||||
err_msg := 'IError_name_table[${cvar_name}.err._typ]._method_msg(${cvar_name}.err._object)'
|
||||
|
@ -5004,6 +5135,35 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty
|
|||
g.writeln('\treturn $err_obj;')
|
||||
}
|
||||
}
|
||||
} else if or_block.kind == .propagate_result {
|
||||
if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.is_main) {
|
||||
// In main(), an `opt()!` call is sugar for `opt() or { panic(err) }`
|
||||
err_msg := 'IError_name_table[${cvar_name}.err._typ]._method_msg(${cvar_name}.err._object)'
|
||||
if g.pref.is_debug {
|
||||
paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos)
|
||||
g.writeln('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), $err_msg);')
|
||||
} else {
|
||||
g.writeln('\tpanic_result_not_set($err_msg);')
|
||||
}
|
||||
} else if !isnil(g.fn_decl) && g.fn_decl.is_test {
|
||||
g.gen_failing_error_propagation_for_test_fn(or_block, cvar_name)
|
||||
} else {
|
||||
// In ordinary functions, `opt()!` call is sugar for:
|
||||
// `opt() or { return err }`
|
||||
// Since we *do* return, first we have to ensure that
|
||||
// the defered statements are generated.
|
||||
g.write_defer_stmts()
|
||||
// Now that option types are distinct we need a cast here
|
||||
if g.fn_decl.return_type == ast.void_type {
|
||||
g.writeln('\treturn;')
|
||||
} else {
|
||||
styp := g.typ(g.fn_decl.return_type)
|
||||
err_obj := g.new_tmp_var()
|
||||
g.writeln('\t$styp $err_obj;')
|
||||
g.writeln('\tmemcpy(&$err_obj, &$cvar_name, sizeof(result));')
|
||||
g.writeln('\treturn $err_obj;')
|
||||
}
|
||||
}
|
||||
}
|
||||
g.writeln('}')
|
||||
g.set_current_pos_as_last_stmt_pos()
|
||||
|
|
|
@ -671,9 +671,6 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
|||
tmp_opt := if gen_or || gen_keep_alive { g.new_tmp_var() } else { '' }
|
||||
if gen_or || gen_keep_alive {
|
||||
mut ret_typ := node.return_type
|
||||
if gen_or {
|
||||
ret_typ = ret_typ.set_flag(.optional)
|
||||
}
|
||||
styp := g.typ(ret_typ)
|
||||
if gen_or && !is_gen_or_and_assign_rhs {
|
||||
cur_line = g.go_before_stmt(0)
|
||||
|
@ -695,7 +692,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
|||
// if !g.is_autofree {
|
||||
g.or_block(tmp_opt, node.or_block, node.return_type)
|
||||
//}
|
||||
unwrapped_typ := node.return_type.clear_flag(.optional)
|
||||
unwrapped_typ := node.return_type.clear_flag(.optional).clear_flag(.result)
|
||||
unwrapped_styp := g.typ(unwrapped_typ)
|
||||
if unwrapped_typ == ast.void_type {
|
||||
g.write('\n $cur_line')
|
||||
|
|
|
@ -17,13 +17,14 @@ fn (mut g Gen) str_format(node ast.StringInterLiteral, i int) (u64, string) {
|
|||
mut upper_case := false // set upercase for the result string
|
||||
mut typ := g.unwrap_generic(node.expr_types[i])
|
||||
sym := g.table.sym(typ)
|
||||
|
||||
if sym.kind == .alias {
|
||||
typ = (sym.info as ast.Alias).parent_type
|
||||
}
|
||||
mut remove_tail_zeros := false
|
||||
fspec := node.fmts[i]
|
||||
mut fmt_type := StrIntpType{}
|
||||
|
||||
g.write('/*$fspec $sym*/')
|
||||
// upper cases
|
||||
if (fspec - `A`) <= (`Z` - `A`) {
|
||||
upper_case = true
|
||||
|
|
|
@ -413,6 +413,8 @@ fn (mut g JsGen) fn_decl_str(info ast.FnType) string {
|
|||
fn_str += ')'
|
||||
if info.func.return_type == ast.ovoid_type {
|
||||
fn_str += ' ?'
|
||||
} else if info.func.return_type == ast.rvoid_type {
|
||||
fn_str += ' !'
|
||||
} else if info.func.return_type != ast.void_type {
|
||||
x := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.func.return_type)))
|
||||
if info.func.return_type.has_flag(.optional) {
|
||||
|
|
|
@ -116,7 +116,7 @@ fn (mut g JsGen) js_call(node ast.CallExpr) {
|
|||
// g.write('return ')
|
||||
g.stmt(it.or_block.stmts.last())
|
||||
}
|
||||
.propagate {
|
||||
.propagate_option {
|
||||
panicstr := '`optional not set (\${err + ""})`'
|
||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||
g.writeln('return builtin__panic($panicstr)')
|
||||
|
@ -176,12 +176,12 @@ fn (mut g JsGen) js_method_call(node ast.CallExpr) {
|
|||
// g.write('return ')
|
||||
g.stmt(it.or_block.stmts.last())
|
||||
}
|
||||
.propagate {
|
||||
.propagate_option {
|
||||
panicstr := '`optional not set (\${err + ""})`'
|
||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||
g.writeln('return builtin__panic($panicstr)')
|
||||
} else {
|
||||
g.writeln('throw new Option({ state: new u8(2), err: error(new string($panicstr)) });')
|
||||
g.writeln('throw new option({ state: new u8(2), err: error(new string($panicstr)) });')
|
||||
}
|
||||
}
|
||||
else {}
|
||||
|
@ -367,7 +367,7 @@ fn (mut g JsGen) method_call(node ast.CallExpr) {
|
|||
// g.write('return ')
|
||||
g.stmt(it.or_block.stmts.last())
|
||||
}
|
||||
.propagate {
|
||||
.propagate_option {
|
||||
panicstr := '`optional not set (\${err.valueOf().msg})`'
|
||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||
g.writeln('return builtin__panic($panicstr)')
|
||||
|
@ -468,7 +468,7 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
|
|||
// g.write('return ')
|
||||
g.stmt(it.or_block.stmts.last())
|
||||
}
|
||||
.propagate {
|
||||
.propagate_option {
|
||||
panicstr := '`optional not set (\${err.valueOf().msg})`'
|
||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||
g.writeln('return builtin__panic($panicstr)')
|
||||
|
|
|
@ -1183,7 +1183,7 @@ fn (mut g JsGen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) {
|
|||
if expr is ast.CTempVar {
|
||||
if expr.orig is ast.CallExpr {
|
||||
should_clone = false
|
||||
if expr.orig.or_block.kind == .propagate {
|
||||
if expr.orig.or_block.kind == .propagate_option {
|
||||
should_clone = true
|
||||
}
|
||||
if expr.orig.is_method && expr.orig.args.len == 0
|
||||
|
|
|
@ -535,7 +535,7 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr {
|
|||
}
|
||||
if p.tok.kind == .question {
|
||||
p.next()
|
||||
or_kind = .propagate
|
||||
or_kind = .propagate_option
|
||||
}
|
||||
p.or_is_handled = false
|
||||
}
|
||||
|
@ -627,7 +627,7 @@ fn (mut p Parser) prefix_expr() ast.Expr {
|
|||
}
|
||||
if p.tok.kind == .question {
|
||||
p.next()
|
||||
or_kind = .propagate
|
||||
or_kind = .propagate_option
|
||||
}
|
||||
p.or_is_handled = false
|
||||
}
|
||||
|
|
|
@ -45,10 +45,6 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr
|
|||
args := p.call_args()
|
||||
last_pos := p.tok.pos()
|
||||
p.check(.rpar)
|
||||
// ! in mutable methods
|
||||
if p.tok.kind == .not {
|
||||
p.next()
|
||||
}
|
||||
mut pos := first_pos.extend(last_pos)
|
||||
mut or_stmts := []ast.Stmt{} // TODO remove unnecessary allocations by just using .absent
|
||||
mut or_pos := p.tok.pos()
|
||||
|
@ -70,13 +66,14 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr
|
|||
p.close_scope()
|
||||
p.inside_or_expr = was_inside_or_expr
|
||||
}
|
||||
if p.tok.kind == .question {
|
||||
if p.tok.kind in [.question, .not] {
|
||||
is_not := p.tok.kind == .not
|
||||
// `foo()?`
|
||||
p.next()
|
||||
if p.inside_defer {
|
||||
p.error_with_pos('error propagation not allowed inside `defer` blocks', p.prev_tok.pos())
|
||||
}
|
||||
or_kind = .propagate
|
||||
or_kind = if is_not { .propagate_result } else { .propagate_option }
|
||||
}
|
||||
if fn_name in p.imported_symbols {
|
||||
fn_name = p.imported_symbols[fn_name]
|
||||
|
|
|
@ -375,19 +375,25 @@ pub fn (mut p Parser) parse_sum_type_variants() []ast.TypeNode {
|
|||
pub fn (mut p Parser) parse_type() ast.Type {
|
||||
// optional
|
||||
mut is_optional := false
|
||||
mut is_result := false
|
||||
line_nr := p.tok.line_nr
|
||||
optional_pos := p.tok.pos()
|
||||
if p.tok.kind == .question {
|
||||
line_nr := p.tok.line_nr
|
||||
p.next()
|
||||
is_optional = true
|
||||
if p.tok.line_nr > line_nr {
|
||||
} else if p.tok.kind == .not {
|
||||
p.next()
|
||||
is_result = true
|
||||
}
|
||||
if (is_optional || is_result) && p.tok.line_nr > line_nr {
|
||||
mut typ := ast.void_type
|
||||
if is_optional {
|
||||
typ = typ.set_flag(.optional)
|
||||
} else if is_result {
|
||||
typ = typ.set_flag(.result)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
}
|
||||
is_shared := p.tok.kind == .key_shared
|
||||
is_atomic := p.tok.kind == .key_atomic
|
||||
if is_shared {
|
||||
|
@ -441,6 +447,9 @@ pub fn (mut p Parser) parse_type() ast.Type {
|
|||
if is_optional {
|
||||
typ = typ.set_flag(.optional)
|
||||
}
|
||||
if is_result {
|
||||
typ = typ.set_flag(.result)
|
||||
}
|
||||
if is_shared {
|
||||
typ = typ.set_flag(.shared_f)
|
||||
}
|
||||
|
|
|
@ -2478,7 +2478,7 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr {
|
|||
// `a[start..end] ?`
|
||||
if p.tok.kind == .question {
|
||||
or_pos_high = p.tok.pos()
|
||||
or_kind_high = .propagate
|
||||
or_kind_high = .propagate_option
|
||||
p.next()
|
||||
}
|
||||
}
|
||||
|
@ -2552,7 +2552,7 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr {
|
|||
// `a[start..end] ?`
|
||||
if p.tok.kind == .question {
|
||||
or_pos_low = p.tok.pos()
|
||||
or_kind_low = .propagate
|
||||
or_kind_low = .propagate_option
|
||||
p.next()
|
||||
}
|
||||
}
|
||||
|
@ -2609,7 +2609,7 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr {
|
|||
// `a[i] ?`
|
||||
if p.tok.kind == .question {
|
||||
or_pos = p.tok.pos()
|
||||
or_kind = .propagate
|
||||
or_kind = .propagate_option
|
||||
p.next()
|
||||
}
|
||||
}
|
||||
|
@ -2711,15 +2711,15 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
|||
p.inside_or_expr = was_inside_or_expr
|
||||
}
|
||||
// `foo()?`
|
||||
if p.tok.kind == .question {
|
||||
if p.tok.kind in [.question, .not] {
|
||||
is_not := p.tok.kind == .not
|
||||
p.next()
|
||||
if p.inside_defer {
|
||||
p.error_with_pos('error propagation not allowed inside `defer` blocks',
|
||||
p.prev_tok.pos())
|
||||
}
|
||||
or_kind = .propagate
|
||||
or_kind = if is_not { .propagate_result } else { .propagate_option }
|
||||
}
|
||||
//
|
||||
end_pos := p.prev_tok.pos()
|
||||
pos := name_pos.extend(end_pos)
|
||||
comments := p.eat_comments(same_line: true)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
vlib/v/parser/tests/option_result_err.vv:2:2: error: invalid expression: unexpected keyword `return`
|
||||
1 | fn abc() ?!string {
|
||||
2 | return ''
|
||||
| ~~~~~~
|
||||
3 | }
|
|
@ -0,0 +1,3 @@
|
|||
fn abc() ?!string {
|
||||
return ''
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
vlib/v/parser/tests/result_option_err.vv:2:2: error: invalid expression: unexpected keyword `return`
|
||||
1 | fn abc() ?!string {
|
||||
2 | return ''
|
||||
| ~~~~~~
|
||||
3 | }
|
|
@ -0,0 +1,3 @@
|
|||
fn abc() ?!string {
|
||||
return ''
|
||||
}
|
|
@ -1023,7 +1023,7 @@ fn (mut s Scanner) text_scan() token.Token {
|
|||
s.pos += 2
|
||||
return s.new_token(.not_is, '', 3)
|
||||
} else {
|
||||
return s.new_token(.not, '', 1)
|
||||
return s.new_token(.not, '!', 1)
|
||||
}
|
||||
}
|
||||
`~` {
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
fn foo() !int {
|
||||
return 1
|
||||
}
|
||||
|
||||
fn test_return_int() {
|
||||
x := foo() or { 0 }
|
||||
assert x == 1
|
||||
}
|
||||
|
||||
fn foo_err() !int {
|
||||
return error('throw')
|
||||
}
|
||||
|
||||
fn test_return_err() {
|
||||
x := foo_err() or { 0 }
|
||||
assert x == 0
|
||||
}
|
||||
|
||||
fn test_return_err_var() {
|
||||
foo_err() or { assert err.msg() == 'throw' }
|
||||
}
|
||||
|
||||
fn test_str() {
|
||||
assert '$foo()' == 'result(1)'
|
||||
}
|
||||
|
||||
fn result_void(err bool) ! {
|
||||
if err {
|
||||
return error('throw')
|
||||
}
|
||||
}
|
||||
|
||||
fn test_result_void() {
|
||||
result_void(false) or { assert false }
|
||||
}
|
||||
|
||||
fn test_result_void_err() {
|
||||
mut or_block := false
|
||||
result_void(true) or {
|
||||
assert err.msg() == 'throw'
|
||||
or_block = true
|
||||
}
|
||||
assert or_block
|
||||
}
|
||||
|
||||
fn propagate() ! {
|
||||
result_void(false) !
|
||||
}
|
||||
|
||||
fn test_propagation() {
|
||||
propagate() or { assert false }
|
||||
}
|
||||
|
||||
fn function_that_can_return_error() !int {
|
||||
return error('abc')
|
||||
}
|
||||
|
||||
fn util_error_propagation() ! {
|
||||
function_that_can_return_error() !
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_return_on_error_propagation() {
|
||||
util_error_propagation() or { assert err.msg() == 'abc' }
|
||||
}
|
|
@ -479,7 +479,7 @@ pub fn (tok Kind) is_relational() bool {
|
|||
|
||||
[inline]
|
||||
pub fn (k Kind) is_start_of_type() bool {
|
||||
return k in [.name, .lpar, .amp, .lsbr, .question, .key_shared]
|
||||
return k in [.name, .lpar, .amp, .lsbr, .question, .key_shared, .not]
|
||||
}
|
||||
|
||||
[inline]
|
||||
|
|
Loading…
Reference in New Issue