all: basic implementation of result type (#14140)
parent
3e4834aebf
commit
85cb0a8c85
|
@ -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 {
|
pub fn (_ none) str() string {
|
||||||
return 'none'
|
return 'none'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1487,7 +1487,8 @@ pub mut:
|
||||||
pub enum OrKind {
|
pub enum OrKind {
|
||||||
absent
|
absent
|
||||||
block
|
block
|
||||||
propagate
|
propagate_option
|
||||||
|
propagate_result
|
||||||
}
|
}
|
||||||
|
|
||||||
// `or { ... }`
|
// `or { ... }`
|
||||||
|
|
|
@ -210,7 +210,8 @@ pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) {
|
||||||
}
|
}
|
||||||
CallExpr {
|
CallExpr {
|
||||||
if sub_expr.args.len != 0 || sub_expr.concrete_types.len != 0
|
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
|
needs_braces = true
|
||||||
} else if sub_expr.left is CallExpr {
|
} else if sub_expr.left is CallExpr {
|
||||||
sub_expr = sub_expr.left
|
sub_expr = sub_expr.left
|
||||||
|
@ -302,7 +303,7 @@ pub fn (x Expr) str() string {
|
||||||
}
|
}
|
||||||
CallExpr {
|
CallExpr {
|
||||||
sargs := args2str(x.args)
|
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 {
|
if x.is_method {
|
||||||
return '${x.left.str()}.${x.name}($sargs)$propagate_suffix'
|
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
|
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 {
|
pub fn (t &Table) fn_type_source_signature(f &Fn) string {
|
||||||
mut sig := '('
|
mut sig := '('
|
||||||
for i, arg in f.params {
|
for i, arg in f.params {
|
||||||
|
@ -252,10 +252,14 @@ pub fn (t &Table) fn_type_source_signature(f &Fn) string {
|
||||||
sig += ')'
|
sig += ')'
|
||||||
if f.return_type == ovoid_type {
|
if f.return_type == ovoid_type {
|
||||||
sig += ' ?'
|
sig += ' ?'
|
||||||
|
} else if f.return_type == rvoid_type {
|
||||||
|
sig += ' !'
|
||||||
} else if f.return_type != void_type {
|
} else if f.return_type != void_type {
|
||||||
return_type_sym := t.sym(f.return_type)
|
return_type_sym := t.sym(f.return_type)
|
||||||
if f.return_type.has_flag(.optional) {
|
if f.return_type.has_flag(.optional) {
|
||||||
sig += ' ?$return_type_sym.name'
|
sig += ' ?$return_type_sym.name'
|
||||||
|
} else if f.return_type.has_flag(.result) {
|
||||||
|
sig += ' !$return_type_sym.name'
|
||||||
} else {
|
} else {
|
||||||
sig += ' $return_type_sym.name'
|
sig += ' $return_type_sym.name'
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,7 @@ pub mut:
|
||||||
// max of 8
|
// max of 8
|
||||||
pub enum TypeFlag {
|
pub enum TypeFlag {
|
||||||
optional
|
optional
|
||||||
|
result
|
||||||
variadic
|
variadic
|
||||||
generic
|
generic
|
||||||
shared_f
|
shared_f
|
||||||
|
@ -472,6 +473,7 @@ pub const (
|
||||||
pub const (
|
pub const (
|
||||||
void_type = new_type(void_type_idx)
|
void_type = new_type(void_type_idx)
|
||||||
ovoid_type = new_type(void_type_idx).set_flag(.optional) // the return type of `fn () ?`
|
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)
|
voidptr_type = new_type(voidptr_type_idx)
|
||||||
byteptr_type = new_type(byteptr_type_idx)
|
byteptr_type = new_type(byteptr_type_idx)
|
||||||
charptr_type = new_type(charptr_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) {
|
if typ.has_flag(.optional) {
|
||||||
return '?'
|
return '?'
|
||||||
}
|
}
|
||||||
|
if typ.has_flag(.result) {
|
||||||
|
return '!'
|
||||||
|
}
|
||||||
return 'void'
|
return 'void'
|
||||||
}
|
}
|
||||||
.thread {
|
.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) {
|
if typ.has_flag(.optional) {
|
||||||
res = '?$res'
|
res = '?$res'
|
||||||
}
|
}
|
||||||
|
if typ.has_flag(.result) {
|
||||||
|
res = '!$res'
|
||||||
|
}
|
||||||
return 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() {
|
if expected == ast.charptr_type && got == ast.char_type.ref() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if expected.has_flag(.optional) {
|
if expected.has_flag(.optional) || expected.has_flag(.result) {
|
||||||
sym := c.table.sym(got)
|
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
|
// IErorr
|
||||||
return true
|
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
|
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
|
// 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 {
|
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 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 expr.or_block.kind == .absent {
|
||||||
if c.inside_defer {
|
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)
|
expr.pos)
|
||||||
} else {
|
} 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)
|
expr.pos)
|
||||||
}
|
}
|
||||||
} else {
|
} 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)
|
return ret_type.clear_flag(.optional)
|
||||||
} else if expr.or_block.kind == .block {
|
} 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)
|
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',
|
c.error('unexpected `?`, the function `$expr.name` does not return an optional',
|
||||||
expr.or_block.pos)
|
expr.or_block.pos)
|
||||||
}
|
}
|
||||||
} else if expr is ast.IndexExpr {
|
} else if expr is ast.IndexExpr {
|
||||||
if expr.or_expr.kind != .absent {
|
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
|
return ret_type
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_return_type ast.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'
|
if !c.table.cur_fn.return_type.has_flag(.optional) && c.table.cur_fn.name != 'main.main'
|
||||||
&& !c.inside_const {
|
&& !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)
|
node.pos)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -1535,7 +1557,7 @@ pub fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_re
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
last_stmt := node.stmts[stmts_len - 1]
|
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) {
|
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 {
|
ast.CallExpr {
|
||||||
mut ret_type := c.call_expr(mut node)
|
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 {
|
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)
|
node.or_block.pos)
|
||||||
} else if node.or_block.kind == .propagate {
|
} else if node.or_block.kind == .propagate_option {
|
||||||
c.error('unexpected `?`, the function `$node.name` does not return an optional',
|
c.error('unexpected `?`, the function `$node.name` does neither return an optional nor a result',
|
||||||
node.or_block.pos)
|
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)
|
ret_type = ret_type.clear_flag(.optional)
|
||||||
}
|
}
|
||||||
|
if ret_type.has_flag(.result) {
|
||||||
|
ret_type = ret_type.clear_flag(.result)
|
||||||
|
}
|
||||||
|
}
|
||||||
return ret_type
|
return ret_type
|
||||||
}
|
}
|
||||||
ast.ChanInit {
|
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.fn_scope = node.scope
|
||||||
c.stmts(node.stmts)
|
c.stmts(node.stmts)
|
||||||
node_has_top_return := has_top_return(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.expected_or_type = node.return_type.clear_flag(.optional)
|
||||||
c.stmts_ending_with_expression(node.or_block.stmts)
|
c.stmts_ending_with_expression(node.or_block.stmts)
|
||||||
c.expected_or_type = ast.void_type
|
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 {
|
&& !c.inside_const {
|
||||||
if !c.table.cur_fn.is_main {
|
if !c.table.cur_fn.is_main {
|
||||||
c.error('to propagate the optional call, `$c.table.cur_fn.name` must return an optional',
|
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 {
|
if exp_is_optional && node.exprs.len > 0 {
|
||||||
expr0 := node.exprs[0]
|
expr0 := node.exprs[0]
|
||||||
if expr0 is ast.CallExpr {
|
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)
|
c.error('`?` is not needed, use `return ${expr0.name}()`', expr0.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import v.ast
|
||||||
import v.token
|
import v.token
|
||||||
|
|
||||||
pub fn (mut c Checker) get_default_fmt(ftyp ast.Type, typ ast.Type) u8 {
|
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`
|
return `s`
|
||||||
} else if typ.is_float() {
|
} else if typ.is_float() {
|
||||||
return `g`
|
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 |
|
4 |
|
||||||
5 | pub fn next(mut v []Typ) Typ {
|
5 | pub fn next(mut v []Typ) Typ {
|
||||||
6 | return v.pop() or { 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)
|
9 | go d(1)
|
||||||
10 | ]
|
10 | ]
|
||||||
11 | r := tg.wait() ?
|
11 | r := tg.wait() ?
|
||||||
| ^
|
| ^
|
||||||
12 | println(r)
|
12 | println(r)
|
||||||
13 | s := tg[0].wait() or { panic('problem') }
|
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() ?
|
11 | r := tg.wait() ?
|
||||||
12 | println(r)
|
12 | println(r)
|
||||||
13 | s := tg[0].wait() or { panic('problem') }
|
13 | s := tg[0].wait() or { panic('problem') }
|
||||||
| ~~~~~~~~~~~~~~~~~~~~~~~
|
| ~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
14 | println(s)
|
14 | println(s)
|
||||||
15 | tg2 := [
|
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)
|
17 | go e(1)
|
||||||
18 | ]
|
18 | ]
|
||||||
19 | tg2.wait() or { panic('problem') }
|
19 | tg2.wait() or { panic('problem') }
|
||||||
| ~~~~~~~~~~~~~~~~~~~~~~~
|
| ~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
20 | tg2[0].wait() ?
|
20 | tg2[0].wait() ?
|
||||||
21 | tg3 := [
|
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 | ]
|
18 | ]
|
||||||
19 | tg2.wait() or { panic('problem') }
|
19 | tg2.wait() or { panic('problem') }
|
||||||
20 | tg2[0].wait() ?
|
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 |
|
4 |
|
||||||
5 | fn main() {
|
5 | fn main() {
|
||||||
6 | _ = ret_zero() or { 1 }
|
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 |
|
4 |
|
||||||
5 | fn opt_fn() ?int {
|
5 | fn opt_fn() ?int {
|
||||||
6 | a := ret_zero()?
|
6 | a := ret_zero()?
|
||||||
|
|
|
@ -1335,6 +1335,8 @@ pub fn (mut f Fmt) fn_type_decl(node ast.FnTypeDecl) {
|
||||||
f.write(' $ret_str')
|
f.write(' $ret_str')
|
||||||
} else if fn_info.return_type.has_flag(.optional) {
|
} else if fn_info.return_type.has_flag(.optional) {
|
||||||
f.write(' ?')
|
f.write(' ?')
|
||||||
|
} else if fn_info.return_type.has_flag(.result) {
|
||||||
|
f.write(' !')
|
||||||
}
|
}
|
||||||
|
|
||||||
f.comments(node.comments, has_nl: false)
|
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.stmts(node.stmts)
|
||||||
f.write('}')
|
f.write('}')
|
||||||
}
|
}
|
||||||
.propagate {
|
.propagate_option {
|
||||||
f.write(' ?')
|
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 is ast.CTempVar {
|
||||||
if expr.orig is ast.CallExpr {
|
if expr.orig is ast.CallExpr {
|
||||||
should_clone = false
|
should_clone = false
|
||||||
if expr.orig.or_block.kind == .propagate {
|
if expr.orig.or_block.kind == .propagate_option {
|
||||||
should_clone = true
|
should_clone = true
|
||||||
}
|
}
|
||||||
if expr.orig.is_method && expr.orig.args.len == 0
|
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
|
g.generated_str_fns << typ
|
||||||
sym := g.table.sym(typ.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
|
return
|
||||||
}
|
}
|
||||||
styp := typ.styp
|
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)
|
g.gen_str_for_option(typ.typ, styp, str_fn_name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if typ.typ.has_flag(.result) {
|
||||||
|
g.gen_str_for_result(typ.typ, styp, str_fn_name)
|
||||||
|
return
|
||||||
|
}
|
||||||
match sym.info {
|
match sym.info {
|
||||||
ast.Alias {
|
ast.Alias {
|
||||||
if sym.info.is_import {
|
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('}')
|
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) {
|
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)
|
parent_str_fn_name := g.get_str_fn(info.parent_type)
|
||||||
$if trace_autostr ? {
|
$if trace_autostr ? {
|
||||||
|
@ -506,6 +544,8 @@ fn (mut g Gen) fn_decl_str(info ast.FnType) string {
|
||||||
fn_str += ')'
|
fn_str += ')'
|
||||||
if info.func.return_type == ast.ovoid_type {
|
if info.func.return_type == ast.ovoid_type {
|
||||||
fn_str += ' ?'
|
fn_str += ' ?'
|
||||||
|
} else if info.func.return_type == ast.rvoid_type {
|
||||||
|
fn_str += ' !'
|
||||||
} else if info.func.return_type != ast.void_type {
|
} 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)))
|
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) {
|
if info.func.return_type.has_flag(.optional) {
|
||||||
|
|
|
@ -73,7 +73,8 @@ mut:
|
||||||
embedded_data strings.Builder // data to embed in the executable/binary
|
embedded_data strings.Builder // data to embed in the executable/binary
|
||||||
shared_types strings.Builder // shared/lock types
|
shared_types strings.Builder // shared/lock types
|
||||||
shared_functions strings.Builder // shared constructors
|
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
|
json_forward_decls strings.Builder // json type forward decls
|
||||||
sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
|
sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
|
||||||
file &ast.File
|
file &ast.File
|
||||||
|
@ -100,6 +101,7 @@ mut:
|
||||||
is_cc_msvc bool // g.pref.ccompiler == 'msvc'
|
is_cc_msvc bool // g.pref.ccompiler == 'msvc'
|
||||||
vlines_path string // set to the proper path for generating #line directives
|
vlines_path string // set to the proper path for generating #line directives
|
||||||
optionals map[string]string // to avoid duplicates
|
optionals map[string]string // to avoid duplicates
|
||||||
|
results map[string]string // to avoid duplicates
|
||||||
done_optionals shared []string // to avoid duplicates
|
done_optionals shared []string // to avoid duplicates
|
||||||
chan_pop_optionals map[string]string // types for `x := <-ch or {...}`
|
chan_pop_optionals map[string]string // types for `x := <-ch or {...}`
|
||||||
chan_push_optionals map[string]string // types for `ch <- x 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)
|
pcs_declarations: strings.new_builder(100)
|
||||||
embedded_data: strings.new_builder(1000)
|
embedded_data: strings.new_builder(1000)
|
||||||
options: strings.new_builder(100)
|
options: strings.new_builder(100)
|
||||||
|
out_results: strings.new_builder(100)
|
||||||
shared_types: strings.new_builder(100)
|
shared_types: strings.new_builder(100)
|
||||||
shared_functions: strings.new_builder(100)
|
shared_functions: strings.new_builder(100)
|
||||||
json_forward_decls: 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 {
|
for k, v in g.optionals {
|
||||||
global_g.optionals[k] = v
|
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 {
|
for k, v in g.as_cast_type_names {
|
||||||
global_g.as_cast_type_names[k] = v
|
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.gen_jsons()
|
||||||
global_g.write_optionals()
|
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
|
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++ {
|
for i := 0; i < global_g.str_types.len; i++ {
|
||||||
global_g.final_gen_str(global_g.str_types[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.write_string(g.shared_types.str())
|
||||||
b.writeln('\n// V Option_xxx definitions:')
|
b.writeln('\n// V Option_xxx definitions:')
|
||||||
b.write_string(g.options.str())
|
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.writeln('\n// V json forward decls:')
|
||||||
b.write_string(g.json_forward_decls.str())
|
b.write_string(g.json_forward_decls.str())
|
||||||
b.writeln('\n// V definitions:')
|
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)
|
hotcode_definitions: strings.new_builder(100)
|
||||||
embedded_data: strings.new_builder(1000)
|
embedded_data: strings.new_builder(1000)
|
||||||
options: strings.new_builder(100)
|
options: strings.new_builder(100)
|
||||||
|
out_results: strings.new_builder(100)
|
||||||
shared_types: strings.new_builder(100)
|
shared_types: strings.new_builder(100)
|
||||||
shared_functions: strings.new_builder(100)
|
shared_functions: strings.new_builder(100)
|
||||||
channel_definitions: 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.shared_functions.free()
|
||||||
g.channel_definitions.free()
|
g.channel_definitions.free()
|
||||||
g.options.free()
|
g.options.free()
|
||||||
|
g.out_results.free()
|
||||||
g.json_forward_decls.free()
|
g.json_forward_decls.free()
|
||||||
g.enum_typedefs.free()
|
g.enum_typedefs.free()
|
||||||
g.sql_buf.free()
|
g.sql_buf.free()
|
||||||
|
@ -884,6 +895,8 @@ fn (mut g Gen) typ(t ast.Type) string {
|
||||||
if t.has_flag(.optional) {
|
if t.has_flag(.optional) {
|
||||||
// Register an optional if it's not registered yet
|
// Register an optional if it's not registered yet
|
||||||
return g.register_optional(t)
|
return g.register_optional(t)
|
||||||
|
} else if t.has_flag(.result) {
|
||||||
|
return g.register_result(t)
|
||||||
} else {
|
} else {
|
||||||
return g.base_type(t)
|
return g.base_type(t)
|
||||||
}
|
}
|
||||||
|
@ -965,6 +978,15 @@ fn (mut g Gen) optional_type_name(t ast.Type) (string, string) {
|
||||||
return styp, base
|
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 {
|
fn (g Gen) optional_type_text(styp string, base string) string {
|
||||||
// replace void with something else
|
// replace void with something else
|
||||||
size := if base == 'void' {
|
size := if base == 'void' {
|
||||||
|
@ -982,12 +1004,35 @@ fn (g Gen) optional_type_text(styp string, base string) string {
|
||||||
return ret
|
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 {
|
fn (mut g Gen) register_optional(t ast.Type) string {
|
||||||
styp, base := g.optional_type_name(t)
|
styp, base := g.optional_type_name(t)
|
||||||
g.optionals[base] = styp
|
g.optionals[base] = styp
|
||||||
return 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() {
|
fn (mut g Gen) write_optionals() {
|
||||||
mut done := []string{}
|
mut done := []string{}
|
||||||
rlock g.done_optionals {
|
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 {
|
fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string {
|
||||||
g.shareds[t.idx()] = base
|
g.shareds[t.idx()] = base
|
||||||
return '__shared__$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
|
// Register an optional if it's not registered yet
|
||||||
g.register_optional(method.return_type)
|
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
|
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) {
|
fn (mut g Gen) gen_optional_error(target_type ast.Type, expr ast.Expr) {
|
||||||
styp := g.typ(target_type)
|
styp := g.typ(target_type)
|
||||||
g.write('($styp){ .state=2, .err=')
|
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)
|
sym := g.table.sym(g.fn_decl.return_type)
|
||||||
fn_return_is_multi := sym.kind == .multi_return
|
fn_return_is_multi := sym.kind == .multi_return
|
||||||
fn_return_is_optional := g.fn_decl.return_type.has_flag(.optional)
|
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
|
mut has_semicolon := false
|
||||||
if node.exprs.len == 0 {
|
if node.exprs.len == 0 {
|
||||||
g.write_defer_stmts_when_needed()
|
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)
|
styp := g.typ(g.fn_decl.return_type)
|
||||||
g.writeln('return ($styp){0};')
|
g.writeln('return ($styp){0};')
|
||||||
} else {
|
} else {
|
||||||
|
@ -3969,6 +4039,34 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||||
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
|
// regular cases
|
||||||
if fn_return_is_multi && node.exprs.len > 0 && !g.expr_is_multi_return_call(node.exprs[0]) {
|
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) {
|
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)
|
typ_sym := g.table.sym(g.fn_decl.return_type)
|
||||||
mr_info := typ_sym.info as ast.MultiReturn
|
mr_info := typ_sym.info as ast.MultiReturn
|
||||||
mut styp := ''
|
mut styp := ''
|
||||||
if fn_return_is_optional {
|
if fn_return_is_optional || fn_return_is_result {
|
||||||
g.writeln('$ret_typ $tmpvar;')
|
g.writeln('$ret_typ $tmpvar;')
|
||||||
styp = g.base_type(g.fn_decl.return_type)
|
styp = g.base_type(g.fn_decl.return_type)
|
||||||
g.write('opt_ok(&($styp/*X*/[]) { ')
|
g.write('opt_ok(&($styp/*X*/[]) { ')
|
||||||
|
@ -4054,7 +4152,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.write('}')
|
g.write('}')
|
||||||
if fn_return_is_optional {
|
if fn_return_is_optional || fn_return_is_result {
|
||||||
g.writeln(' }, (Option*)(&$tmpvar), sizeof($styp));')
|
g.writeln(' }, (Option*)(&$tmpvar), sizeof($styp));')
|
||||||
g.write_defer_stmts_when_needed()
|
g.write_defer_stmts_when_needed()
|
||||||
g.write('return $tmpvar')
|
g.write('return $tmpvar')
|
||||||
|
@ -4063,7 +4161,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||||
if multi_unpack.len > 0 {
|
if multi_unpack.len > 0 {
|
||||||
g.insert_before_stmt(multi_unpack)
|
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 {
|
if !has_semicolon {
|
||||||
g.writeln(';')
|
g.writeln(';')
|
||||||
}
|
}
|
||||||
|
@ -4109,6 +4207,35 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||||
g.writeln('return $tmpvar;')
|
g.writeln('return $tmpvar;')
|
||||||
return
|
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`
|
// autofree before `return`
|
||||||
// set free_parent_scopes to true, since all variables defined in parent
|
// set free_parent_scopes to true, since all variables defined in parent
|
||||||
// scopes need to be freed before the return
|
// scopes need to be freed before the return
|
||||||
|
@ -4596,7 +4723,7 @@ fn (mut g Gen) write_init_function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
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() {
|
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 {
|
if return_type != 0 && g.table.sym(return_type).kind == .function {
|
||||||
mr_styp = 'voidptr'
|
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*/ ')
|
g.writeln('if (${cvar_name}.state != 0) { /*or block*/ ')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if or_block.kind == .block {
|
if or_block.kind == .block {
|
||||||
g.or_expr_return_type = return_type.clear_flag(.optional)
|
g.or_expr_return_type = return_type.clear_flag(.optional)
|
||||||
if g.inside_or_block {
|
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
|
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) {
|
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) }`
|
// 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)'
|
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;')
|
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.writeln('}')
|
||||||
g.set_current_pos_as_last_stmt_pos()
|
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 { '' }
|
tmp_opt := if gen_or || gen_keep_alive { g.new_tmp_var() } else { '' }
|
||||||
if gen_or || gen_keep_alive {
|
if gen_or || gen_keep_alive {
|
||||||
mut ret_typ := node.return_type
|
mut ret_typ := node.return_type
|
||||||
if gen_or {
|
|
||||||
ret_typ = ret_typ.set_flag(.optional)
|
|
||||||
}
|
|
||||||
styp := g.typ(ret_typ)
|
styp := g.typ(ret_typ)
|
||||||
if gen_or && !is_gen_or_and_assign_rhs {
|
if gen_or && !is_gen_or_and_assign_rhs {
|
||||||
cur_line = g.go_before_stmt(0)
|
cur_line = g.go_before_stmt(0)
|
||||||
|
@ -695,7 +692,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
|
||||||
// if !g.is_autofree {
|
// if !g.is_autofree {
|
||||||
g.or_block(tmp_opt, node.or_block, node.return_type)
|
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)
|
unwrapped_styp := g.typ(unwrapped_typ)
|
||||||
if unwrapped_typ == ast.void_type {
|
if unwrapped_typ == ast.void_type {
|
||||||
g.write('\n $cur_line')
|
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 upper_case := false // set upercase for the result string
|
||||||
mut typ := g.unwrap_generic(node.expr_types[i])
|
mut typ := g.unwrap_generic(node.expr_types[i])
|
||||||
sym := g.table.sym(typ)
|
sym := g.table.sym(typ)
|
||||||
|
|
||||||
if sym.kind == .alias {
|
if sym.kind == .alias {
|
||||||
typ = (sym.info as ast.Alias).parent_type
|
typ = (sym.info as ast.Alias).parent_type
|
||||||
}
|
}
|
||||||
mut remove_tail_zeros := false
|
mut remove_tail_zeros := false
|
||||||
fspec := node.fmts[i]
|
fspec := node.fmts[i]
|
||||||
mut fmt_type := StrIntpType{}
|
mut fmt_type := StrIntpType{}
|
||||||
|
g.write('/*$fspec $sym*/')
|
||||||
// upper cases
|
// upper cases
|
||||||
if (fspec - `A`) <= (`Z` - `A`) {
|
if (fspec - `A`) <= (`Z` - `A`) {
|
||||||
upper_case = true
|
upper_case = true
|
||||||
|
|
|
@ -413,6 +413,8 @@ fn (mut g JsGen) fn_decl_str(info ast.FnType) string {
|
||||||
fn_str += ')'
|
fn_str += ')'
|
||||||
if info.func.return_type == ast.ovoid_type {
|
if info.func.return_type == ast.ovoid_type {
|
||||||
fn_str += ' ?'
|
fn_str += ' ?'
|
||||||
|
} else if info.func.return_type == ast.rvoid_type {
|
||||||
|
fn_str += ' !'
|
||||||
} else if info.func.return_type != ast.void_type {
|
} 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)))
|
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) {
|
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.write('return ')
|
||||||
g.stmt(it.or_block.stmts.last())
|
g.stmt(it.or_block.stmts.last())
|
||||||
}
|
}
|
||||||
.propagate {
|
.propagate_option {
|
||||||
panicstr := '`optional not set (\${err + ""})`'
|
panicstr := '`optional not set (\${err + ""})`'
|
||||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||||
g.writeln('return builtin__panic($panicstr)')
|
g.writeln('return builtin__panic($panicstr)')
|
||||||
|
@ -176,12 +176,12 @@ fn (mut g JsGen) js_method_call(node ast.CallExpr) {
|
||||||
// g.write('return ')
|
// g.write('return ')
|
||||||
g.stmt(it.or_block.stmts.last())
|
g.stmt(it.or_block.stmts.last())
|
||||||
}
|
}
|
||||||
.propagate {
|
.propagate_option {
|
||||||
panicstr := '`optional not set (\${err + ""})`'
|
panicstr := '`optional not set (\${err + ""})`'
|
||||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||||
g.writeln('return builtin__panic($panicstr)')
|
g.writeln('return builtin__panic($panicstr)')
|
||||||
} else {
|
} 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 {}
|
else {}
|
||||||
|
@ -367,7 +367,7 @@ fn (mut g JsGen) method_call(node ast.CallExpr) {
|
||||||
// g.write('return ')
|
// g.write('return ')
|
||||||
g.stmt(it.or_block.stmts.last())
|
g.stmt(it.or_block.stmts.last())
|
||||||
}
|
}
|
||||||
.propagate {
|
.propagate_option {
|
||||||
panicstr := '`optional not set (\${err.valueOf().msg})`'
|
panicstr := '`optional not set (\${err.valueOf().msg})`'
|
||||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||||
g.writeln('return builtin__panic($panicstr)')
|
g.writeln('return builtin__panic($panicstr)')
|
||||||
|
@ -468,7 +468,7 @@ fn (mut g JsGen) gen_call_expr(it ast.CallExpr) {
|
||||||
// g.write('return ')
|
// g.write('return ')
|
||||||
g.stmt(it.or_block.stmts.last())
|
g.stmt(it.or_block.stmts.last())
|
||||||
}
|
}
|
||||||
.propagate {
|
.propagate_option {
|
||||||
panicstr := '`optional not set (\${err.valueOf().msg})`'
|
panicstr := '`optional not set (\${err.valueOf().msg})`'
|
||||||
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' {
|
||||||
g.writeln('return builtin__panic($panicstr)')
|
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 is ast.CTempVar {
|
||||||
if expr.orig is ast.CallExpr {
|
if expr.orig is ast.CallExpr {
|
||||||
should_clone = false
|
should_clone = false
|
||||||
if expr.orig.or_block.kind == .propagate {
|
if expr.orig.or_block.kind == .propagate_option {
|
||||||
should_clone = true
|
should_clone = true
|
||||||
}
|
}
|
||||||
if expr.orig.is_method && expr.orig.args.len == 0
|
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 {
|
if p.tok.kind == .question {
|
||||||
p.next()
|
p.next()
|
||||||
or_kind = .propagate
|
or_kind = .propagate_option
|
||||||
}
|
}
|
||||||
p.or_is_handled = false
|
p.or_is_handled = false
|
||||||
}
|
}
|
||||||
|
@ -627,7 +627,7 @@ fn (mut p Parser) prefix_expr() ast.Expr {
|
||||||
}
|
}
|
||||||
if p.tok.kind == .question {
|
if p.tok.kind == .question {
|
||||||
p.next()
|
p.next()
|
||||||
or_kind = .propagate
|
or_kind = .propagate_option
|
||||||
}
|
}
|
||||||
p.or_is_handled = false
|
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()
|
args := p.call_args()
|
||||||
last_pos := p.tok.pos()
|
last_pos := p.tok.pos()
|
||||||
p.check(.rpar)
|
p.check(.rpar)
|
||||||
// ! in mutable methods
|
|
||||||
if p.tok.kind == .not {
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
mut pos := first_pos.extend(last_pos)
|
mut pos := first_pos.extend(last_pos)
|
||||||
mut or_stmts := []ast.Stmt{} // TODO remove unnecessary allocations by just using .absent
|
mut or_stmts := []ast.Stmt{} // TODO remove unnecessary allocations by just using .absent
|
||||||
mut or_pos := p.tok.pos()
|
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.close_scope()
|
||||||
p.inside_or_expr = was_inside_or_expr
|
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()?`
|
// `foo()?`
|
||||||
p.next()
|
p.next()
|
||||||
if p.inside_defer {
|
if p.inside_defer {
|
||||||
p.error_with_pos('error propagation not allowed inside `defer` blocks', p.prev_tok.pos())
|
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 {
|
if fn_name in p.imported_symbols {
|
||||||
fn_name = p.imported_symbols[fn_name]
|
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 {
|
pub fn (mut p Parser) parse_type() ast.Type {
|
||||||
// optional
|
// optional
|
||||||
mut is_optional := false
|
mut is_optional := false
|
||||||
|
mut is_result := false
|
||||||
|
line_nr := p.tok.line_nr
|
||||||
optional_pos := p.tok.pos()
|
optional_pos := p.tok.pos()
|
||||||
if p.tok.kind == .question {
|
if p.tok.kind == .question {
|
||||||
line_nr := p.tok.line_nr
|
|
||||||
p.next()
|
p.next()
|
||||||
is_optional = true
|
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
|
mut typ := ast.void_type
|
||||||
if is_optional {
|
if is_optional {
|
||||||
typ = typ.set_flag(.optional)
|
typ = typ.set_flag(.optional)
|
||||||
|
} else if is_result {
|
||||||
|
typ = typ.set_flag(.result)
|
||||||
}
|
}
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
}
|
|
||||||
is_shared := p.tok.kind == .key_shared
|
is_shared := p.tok.kind == .key_shared
|
||||||
is_atomic := p.tok.kind == .key_atomic
|
is_atomic := p.tok.kind == .key_atomic
|
||||||
if is_shared {
|
if is_shared {
|
||||||
|
@ -441,6 +447,9 @@ pub fn (mut p Parser) parse_type() ast.Type {
|
||||||
if is_optional {
|
if is_optional {
|
||||||
typ = typ.set_flag(.optional)
|
typ = typ.set_flag(.optional)
|
||||||
}
|
}
|
||||||
|
if is_result {
|
||||||
|
typ = typ.set_flag(.result)
|
||||||
|
}
|
||||||
if is_shared {
|
if is_shared {
|
||||||
typ = typ.set_flag(.shared_f)
|
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] ?`
|
// `a[start..end] ?`
|
||||||
if p.tok.kind == .question {
|
if p.tok.kind == .question {
|
||||||
or_pos_high = p.tok.pos()
|
or_pos_high = p.tok.pos()
|
||||||
or_kind_high = .propagate
|
or_kind_high = .propagate_option
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2552,7 +2552,7 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr {
|
||||||
// `a[start..end] ?`
|
// `a[start..end] ?`
|
||||||
if p.tok.kind == .question {
|
if p.tok.kind == .question {
|
||||||
or_pos_low = p.tok.pos()
|
or_pos_low = p.tok.pos()
|
||||||
or_kind_low = .propagate
|
or_kind_low = .propagate_option
|
||||||
p.next()
|
p.next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2609,7 +2609,7 @@ fn (mut p Parser) index_expr(left ast.Expr, is_gated bool) ast.IndexExpr {
|
||||||
// `a[i] ?`
|
// `a[i] ?`
|
||||||
if p.tok.kind == .question {
|
if p.tok.kind == .question {
|
||||||
or_pos = p.tok.pos()
|
or_pos = p.tok.pos()
|
||||||
or_kind = .propagate
|
or_kind = .propagate_option
|
||||||
p.next()
|
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
|
p.inside_or_expr = was_inside_or_expr
|
||||||
}
|
}
|
||||||
// `foo()?`
|
// `foo()?`
|
||||||
if p.tok.kind == .question {
|
if p.tok.kind in [.question, .not] {
|
||||||
|
is_not := p.tok.kind == .not
|
||||||
p.next()
|
p.next()
|
||||||
if p.inside_defer {
|
if p.inside_defer {
|
||||||
p.error_with_pos('error propagation not allowed inside `defer` blocks',
|
p.error_with_pos('error propagation not allowed inside `defer` blocks',
|
||||||
p.prev_tok.pos())
|
p.prev_tok.pos())
|
||||||
}
|
}
|
||||||
or_kind = .propagate
|
or_kind = if is_not { .propagate_result } else { .propagate_option }
|
||||||
}
|
}
|
||||||
//
|
|
||||||
end_pos := p.prev_tok.pos()
|
end_pos := p.prev_tok.pos()
|
||||||
pos := name_pos.extend(end_pos)
|
pos := name_pos.extend(end_pos)
|
||||||
comments := p.eat_comments(same_line: true)
|
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
|
s.pos += 2
|
||||||
return s.new_token(.not_is, '', 3)
|
return s.new_token(.not_is, '', 3)
|
||||||
} else {
|
} 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]
|
[inline]
|
||||||
pub fn (k Kind) is_start_of_type() bool {
|
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]
|
[inline]
|
||||||
|
|
Loading…
Reference in New Issue