mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
cgen, check, parser: implement first-class option type (#17017)
This commit is contained in:
parent
e066d1d3df
commit
2879c5110c
@ -1459,6 +1459,7 @@ fn (t Tree) ident(node ast.Ident) &Node {
|
||||
obj.add('mut_pos', t.pos(node.mut_pos))
|
||||
obj.add('obj', t.scope_object(node.obj))
|
||||
obj.add('scope', t.number_node(int(node.scope)))
|
||||
obj.add_terse('or_expr', t.or_expr(node.or_expr))
|
||||
return obj
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,16 @@ struct _option {
|
||||
// derived _option_xxx types
|
||||
}
|
||||
|
||||
fn _option_none(data voidptr, mut option _option, size int) {
|
||||
unsafe {
|
||||
*option = _option{
|
||||
state: 2
|
||||
}
|
||||
// use err to get the end of OptionBase and then memcpy into it
|
||||
vmemcpy(&u8(&option.err) + sizeof(IError), data, size)
|
||||
}
|
||||
}
|
||||
|
||||
fn _option_ok(data voidptr, mut option _option, size int) {
|
||||
unsafe {
|
||||
*option = _option{}
|
||||
|
@ -822,13 +822,14 @@ pub:
|
||||
mut_pos token.Pos
|
||||
comptime bool
|
||||
pub mut:
|
||||
scope &Scope = unsafe { nil }
|
||||
obj ScopeObject
|
||||
mod string
|
||||
name string
|
||||
kind IdentKind
|
||||
info IdentInfo
|
||||
is_mut bool // if mut *token* is before name. Use `is_mut()` to lookup mut variable
|
||||
scope &Scope = unsafe { nil }
|
||||
obj ScopeObject
|
||||
mod string
|
||||
name string
|
||||
kind IdentKind
|
||||
info IdentInfo
|
||||
is_mut bool // if mut *token* is before name. Use `is_mut()` to lookup mut variable
|
||||
or_expr OrExpr
|
||||
}
|
||||
|
||||
pub fn (i &Ident) is_mut() bool {
|
||||
|
@ -56,7 +56,7 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||
c.error('unexpected `mut` on right-hand side of assignment', right.mut_pos)
|
||||
}
|
||||
}
|
||||
if mut right is ast.None {
|
||||
if is_decl && mut right is ast.None {
|
||||
c.error('cannot assign a `none` value to a variable', right.pos)
|
||||
}
|
||||
// Handle `left_name := unsafe { none }`
|
||||
@ -112,6 +112,7 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||
}
|
||||
is_blank_ident := left.is_blank_ident()
|
||||
mut left_type := ast.void_type
|
||||
mut var_option := false
|
||||
if !is_decl && !is_blank_ident {
|
||||
if left in [ast.Ident, ast.SelectorExpr] {
|
||||
c.prevent_sum_type_unwrapping_once = true
|
||||
@ -123,6 +124,10 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||
c.is_index_assign = false
|
||||
c.expected_type = c.unwrap_generic(left_type)
|
||||
}
|
||||
if c.inside_comptime_for_field && mut left is ast.ComptimeSelector {
|
||||
left_type = c.comptime_fields_default_type
|
||||
c.expected_type = c.unwrap_generic(left_type)
|
||||
}
|
||||
if node.right_types.len < node.left.len { // first type or multi return types added above
|
||||
old_inside_ref_lit := c.inside_ref_lit
|
||||
if mut left is ast.Ident {
|
||||
@ -149,6 +154,9 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if right.or_expr.kind in [.propagate_option, .block] {
|
||||
right_type = right_type.clear_flag(.option)
|
||||
}
|
||||
} else if right is ast.ComptimeSelector {
|
||||
right_type = c.comptime_fields_default_type
|
||||
}
|
||||
@ -193,6 +201,10 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||
// Make sure the variable is mutable
|
||||
c.fail_if_immutable(left)
|
||||
// left_type = c.expr(left)
|
||||
// if right is ast.None && !left_type.has_flag(.option) {
|
||||
// println(left_type)
|
||||
// c.error('cannot assign a `none` value to a non-option variable', right.pos())
|
||||
// }
|
||||
}
|
||||
if right_type.is_ptr() && left_type.is_ptr() {
|
||||
if mut right is ast.Ident {
|
||||
@ -271,6 +283,9 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||
if ident_var_info.share == .atomic_t {
|
||||
left_type = left_type.set_flag(.atomic_f)
|
||||
}
|
||||
if ident_var_info.is_option {
|
||||
var_option = true
|
||||
}
|
||||
node.left_types[i] = left_type
|
||||
ident_var_info.typ = left_type
|
||||
left.info = ident_var_info
|
||||
@ -618,7 +633,9 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
|
||||
c.error('enums can only be assigned `int` values', right.pos())
|
||||
}
|
||||
} else {
|
||||
c.error('cannot assign to `${left}`: ${err.msg()}', right.pos())
|
||||
if !var_option || (var_option && right_type_unwrapped != ast.none_type) {
|
||||
c.error('cannot assign to `${left}`: ${err.msg()}', right.pos())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -990,12 +990,12 @@ fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Typ
|
||||
}
|
||||
if expr_ret_type.has_flag(.option) || expr_ret_type.has_flag(.result) {
|
||||
return_modifier_kind := if expr_ret_type.has_flag(.option) {
|
||||
'an option'
|
||||
'an Option'
|
||||
} else {
|
||||
'a result'
|
||||
}
|
||||
return_modifier := if expr_ret_type.has_flag(.option) { '?' } else { '!' }
|
||||
if expr.or_block.kind == .absent {
|
||||
if expr_ret_type.has_flag(.result) && expr.or_block.kind == .absent {
|
||||
if c.inside_defer {
|
||||
c.error('${expr.name}() returns ${return_modifier_kind}, so it should have an `or {}` block at the end',
|
||||
expr.pos)
|
||||
@ -1004,14 +1004,16 @@ fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Typ
|
||||
expr.pos)
|
||||
}
|
||||
} else {
|
||||
c.check_or_expr(expr.or_block, ret_type, expr_ret_type)
|
||||
if expr.or_block.kind != .absent {
|
||||
c.check_or_expr(expr.or_block, ret_type, expr_ret_type, expr)
|
||||
}
|
||||
}
|
||||
return ret_type.clear_flag(.option).clear_flag(.result)
|
||||
return ret_type.clear_flag(.result)
|
||||
} else if expr.or_block.kind == .block {
|
||||
c.error('unexpected `or` block, the function `${expr.name}` does not return an option or a result',
|
||||
c.error('unexpected `or` block, the function `${expr.name}` does not return an Option or a result',
|
||||
expr.or_block.pos)
|
||||
} else if expr.or_block.kind == .propagate_option {
|
||||
c.error('unexpected `?`, the function `${expr.name}` does not return an option',
|
||||
c.error('unexpected `?`, the function `${expr.name}` does not return an Option',
|
||||
expr.or_block.pos)
|
||||
} else if expr.or_block.kind == .propagate_result {
|
||||
c.error('unexpected `!`, the function `${expr.name}` does not return a result',
|
||||
@ -1020,12 +1022,12 @@ fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Typ
|
||||
} else if expr is ast.SelectorExpr && c.table.sym(ret_type).kind != .chan {
|
||||
if expr.typ.has_flag(.option) || expr.typ.has_flag(.result) {
|
||||
with_modifier_kind := if expr.typ.has_flag(.option) {
|
||||
'an option'
|
||||
'an Option'
|
||||
} else {
|
||||
'a result'
|
||||
}
|
||||
with_modifier := if expr.typ.has_flag(.option) { '?' } else { '!' }
|
||||
if expr.or_block.kind == .absent {
|
||||
if expr.typ.has_flag(.result) && expr.or_block.kind == .absent {
|
||||
if c.inside_defer {
|
||||
c.error('field `${expr.field_name}` is ${with_modifier_kind}, so it should have an `or {}` block at the end',
|
||||
expr.pos)
|
||||
@ -1034,21 +1036,23 @@ fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Typ
|
||||
expr.pos)
|
||||
}
|
||||
} else {
|
||||
c.check_or_expr(expr.or_block, ret_type, expr.typ)
|
||||
if expr.or_block.kind != .absent {
|
||||
c.check_or_expr(expr.or_block, ret_type, expr.typ, expr)
|
||||
}
|
||||
}
|
||||
return ret_type.clear_flag(.option).clear_flag(.result)
|
||||
return ret_type.clear_flag(.result)
|
||||
} else if expr.or_block.kind == .block {
|
||||
c.error('unexpected `or` block, the field `${expr.field_name}` is neither an option, nor a result',
|
||||
c.error('unexpected `or` block, the field `${expr.field_name}` is neither an Option, nor a result',
|
||||
expr.or_block.pos)
|
||||
} else if expr.or_block.kind == .propagate_option {
|
||||
c.error('unexpected `?`, the field `${expr.field_name}` is not an option',
|
||||
c.error('unexpected `?`, the field `${expr.field_name}` is not an Option',
|
||||
expr.or_block.pos)
|
||||
} else if expr.or_block.kind == .propagate_result {
|
||||
c.error('unexpected `!`, result fields are not supported', 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.set_flag(.result))
|
||||
c.check_or_expr(expr.or_expr, ret_type, ret_type.set_flag(.result), expr)
|
||||
}
|
||||
} else if expr is ast.CastExpr {
|
||||
c.check_expr_opt_call(expr.expr, ret_type)
|
||||
@ -1058,20 +1062,25 @@ fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Typ
|
||||
return ret_type
|
||||
}
|
||||
|
||||
fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_return_type ast.Type) {
|
||||
fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_return_type ast.Type, expr ast.Expr) {
|
||||
if node.kind == .propagate_option {
|
||||
if c.table.cur_fn != unsafe { nil } && !c.table.cur_fn.return_type.has_flag(.option)
|
||||
&& !c.table.cur_fn.is_main && !c.table.cur_fn.is_test && !c.inside_const {
|
||||
c.add_instruction_for_option_type()
|
||||
c.error('to propagate the call, `${c.table.cur_fn.name}` must return an option type',
|
||||
node.pos)
|
||||
if expr is ast.Ident {
|
||||
c.error('to propagate the Option, `${c.table.cur_fn.name}` must return an Option type',
|
||||
expr.pos)
|
||||
} else {
|
||||
c.error('to propagate the call, `${c.table.cur_fn.name}` must return an Option type',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
if !expr_return_type.has_flag(.option) {
|
||||
if expr !is ast.Ident && !expr_return_type.has_flag(.option) {
|
||||
if expr_return_type.has_flag(.result) {
|
||||
c.warn('propagating a result like an option is deprecated, use `foo()!` instead of `foo()?`',
|
||||
c.warn('propagating a result like an Option is deprecated, use `foo()!` instead of `foo()?`',
|
||||
node.pos)
|
||||
} else {
|
||||
c.error('to propagate an option, the call must also return an option type',
|
||||
c.error('to propagate an Option, the call must also return an Option type',
|
||||
node.pos)
|
||||
}
|
||||
}
|
||||
@ -1109,6 +1118,17 @@ fn (mut c Checker) check_or_last_stmt(stmt ast.Stmt, ret_type ast.Type, expr_ret
|
||||
c.expected_type = ret_type
|
||||
c.expected_or_type = ret_type.clear_flag(.option).clear_flag(.result)
|
||||
last_stmt_typ := c.expr(stmt.expr)
|
||||
|
||||
if stmt.expr is ast.Ident {
|
||||
if ret_type.has_flag(.option) && last_stmt_typ.has_flag(.option) {
|
||||
expected_type_name := c.table.type_to_str(expr_return_type)
|
||||
got_type_name := c.table.type_to_str(last_stmt_typ)
|
||||
c.error('`or` block must provide a value of type `${expected_type_name}`, not `${got_type_name}`',
|
||||
stmt.expr.pos)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.expected_or_type = ast.void_type
|
||||
type_fits := c.check_types(last_stmt_typ, ret_type)
|
||||
&& last_stmt_typ.nr_muls() == ret_type.nr_muls()
|
||||
@ -1140,6 +1160,9 @@ fn (mut c Checker) check_or_last_stmt(stmt ast.Stmt, ret_type ast.Type, expr_ret
|
||||
&& c.table.sym(last_stmt_typ).kind == .voidptr {
|
||||
return
|
||||
}
|
||||
if last_stmt_typ == ast.none_type_idx {
|
||||
return
|
||||
}
|
||||
type_name := c.table.type_to_str(last_stmt_typ)
|
||||
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.option).clear_flag(.result))
|
||||
c.error('wrong return type `${type_name}` in the `or {}` block, expected `${expected_type_name}`',
|
||||
@ -1281,7 +1304,7 @@ fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type {
|
||||
node.expr_type = typ
|
||||
if !(node.expr is ast.Ident && (node.expr as ast.Ident).kind == .constant) {
|
||||
if node.expr_type.has_flag(.option) {
|
||||
c.error('cannot access fields of an option, handle the error with `or {...}` or propagate it with `?`',
|
||||
c.error('cannot access fields of an Option, handle the error with `or {...}` or propagate it with `?`',
|
||||
node.pos)
|
||||
} else if node.expr_type.has_flag(.result) {
|
||||
c.error('cannot access fields of a result, handle the error with `or {...}` or propagate it with `!`',
|
||||
@ -2372,7 +2395,7 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
||||
}
|
||||
} else if expr_type_sym.kind == .interface_ && type_sym.kind == .interface_ {
|
||||
c.ensure_type_exists(node.typ, node.pos) or {}
|
||||
} else if node.expr_type != node.typ {
|
||||
} else if node.expr_type.clear_flag(.option) != node.typ.clear_flag(.option) {
|
||||
mut s := 'cannot cast non-sum type `${expr_type_sym.name}` using `as`'
|
||||
if type_sym.kind == .sum_type {
|
||||
s += ' - use e.g. `${type_sym.name}(some_expr)` instead.'
|
||||
@ -2405,13 +2428,13 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
||||
}
|
||||
if !ret_type.has_flag(.option) && !ret_type.has_flag(.result) {
|
||||
if node.or_block.kind == .block {
|
||||
c.error('unexpected `or` block, the function `${node.name}` does not return an option or a result',
|
||||
c.error('unexpected `or` block, the function `${node.name}` does not return an Option or a result',
|
||||
node.or_block.pos)
|
||||
} else if node.or_block.kind == .propagate_option {
|
||||
c.error('unexpected `?`, the function `${node.name}` does not return an option or a result',
|
||||
c.error('unexpected `?`, the function `${node.name}` does not return an Option or a result',
|
||||
node.or_block.pos)
|
||||
} else if node.or_block.kind == .propagate_result {
|
||||
c.error('unexpected `!`, the function `${node.name}` does not return an option or a result',
|
||||
c.error('unexpected `!`, the function `${node.name}` does not return an Option or a result',
|
||||
node.or_block.pos)
|
||||
}
|
||||
}
|
||||
@ -2504,7 +2527,7 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
||||
else {}
|
||||
}
|
||||
if no_opt_or_res {
|
||||
c.error('expression should either return an option or a result', node.expr.pos())
|
||||
c.error('expression should either return an Option or a result', node.expr.pos())
|
||||
}
|
||||
}
|
||||
return ast.bool_type
|
||||
@ -2582,13 +2605,13 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
|
||||
|
||||
if !ret_type.has_flag(.option) && !ret_type.has_flag(.result) {
|
||||
if node.or_block.kind == .block {
|
||||
c.error('unexpected `or` block, the field `${node.field_name}` is neither an option, nor a result',
|
||||
c.error('unexpected `or` block, the field `${node.field_name}` is neither an Option, nor a result',
|
||||
node.or_block.pos)
|
||||
} else if node.or_block.kind == .propagate_option {
|
||||
c.error('unexpected `?`, the field `${node.field_name}` is neither an option, nor a result',
|
||||
c.error('unexpected `?`, the field `${node.field_name}` is neither an Option, nor a result',
|
||||
node.or_block.pos)
|
||||
} else if node.or_block.kind == .propagate_result {
|
||||
c.error('unexpected `!`, the field `${node.field_name}` is neither an option, nor a result',
|
||||
c.error('unexpected `!`, the field `${node.field_name}` is neither an Option, nor a result',
|
||||
node.or_block.pos)
|
||||
}
|
||||
}
|
||||
@ -2707,9 +2730,7 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
|
||||
mut to_sym := c.table.sym(to_type) // type to be used as cast
|
||||
mut final_to_sym := c.table.final_sym(to_type)
|
||||
|
||||
if to_type.has_flag(.option) {
|
||||
c.error('casting to option type is forbidden', node.pos)
|
||||
} else if to_type.has_flag(.result) {
|
||||
if to_type.has_flag(.result) {
|
||||
c.error('casting to result type is forbidden', node.pos)
|
||||
}
|
||||
|
||||
@ -2758,8 +2779,10 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
|
||||
&& !(to_sym.info as ast.Struct).is_typedef {
|
||||
// For now we ignore C typedef because of `C.Window(C.None)` in vlib/clipboard
|
||||
if from_sym.kind == .struct_ && !from_type.is_ptr() {
|
||||
c.warn('casting to struct is deprecated, use e.g. `Struct{...expr}` instead',
|
||||
node.pos)
|
||||
if !to_type.has_flag(.option) {
|
||||
c.warn('casting to struct is deprecated, use e.g. `Struct{...expr}` instead',
|
||||
node.pos)
|
||||
}
|
||||
from_type_info := from_sym.info as ast.Struct
|
||||
to_type_info := to_sym.info as ast.Struct
|
||||
if !c.check_struct_signature(from_type_info, to_type_info) {
|
||||
@ -2816,11 +2839,11 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
|
||||
ft := c.table.type_to_str(from_type)
|
||||
tt := c.table.type_to_str(to_type)
|
||||
c.error('cannot cast type `${ft}` to `${tt}`', node.pos)
|
||||
} else if from_type.has_flag(.option) || from_type.has_flag(.result)
|
||||
|| from_type.has_flag(.variadic) {
|
||||
} else if (from_type.has_flag(.option) && !to_type.has_flag(.option))
|
||||
|| from_type.has_flag(.result) || from_type.has_flag(.variadic) {
|
||||
// variadic case can happen when arrays are converted into variadic
|
||||
msg := if from_type.has_flag(.option) {
|
||||
'an option'
|
||||
'an Option'
|
||||
} else if from_type.has_flag(.result) {
|
||||
'a result'
|
||||
} else {
|
||||
@ -2848,10 +2871,17 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
|
||||
snexpr := node.expr.str()
|
||||
tt := c.table.type_to_str(to_type)
|
||||
c.error('cannot cast string to `${tt}`, use `${snexpr}[index]` instead.', node.pos)
|
||||
} else if final_from_sym.kind == .array && !from_type.is_ptr() && to_type != ast.string_type {
|
||||
} else if final_from_sym.kind == .array && !from_type.is_ptr() && to_type != ast.string_type
|
||||
&& !(to_type.has_flag(.option) && from_type.idx() == to_type.idx()) {
|
||||
ft := c.table.type_to_str(from_type)
|
||||
tt := c.table.type_to_str(to_type)
|
||||
c.error('cannot cast array `${ft}` to `${tt}`', node.pos)
|
||||
} else if from_type.has_flag(.option) && to_type.has_flag(.option)
|
||||
&& to_sym.kind != final_from_sym.kind {
|
||||
ft := c.table.type_to_str(from_type)
|
||||
tt := c.table.type_to_str(to_type)
|
||||
c.error('cannot cast incompatible option ${final_to_sym.name} `${ft}` to `${tt}`',
|
||||
node.pos)
|
||||
}
|
||||
|
||||
if to_sym.kind == .rune && from_sym.is_string() {
|
||||
@ -3101,6 +3131,16 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
|
||||
if node.kind in [.constant, .global, .variable] {
|
||||
info := node.info as ast.IdentVar
|
||||
// Got a var with type T, return current generic type
|
||||
if node.or_expr.kind != .absent {
|
||||
if !info.typ.has_flag(.option) && node.or_expr.kind == .propagate_option {
|
||||
c.error('cannot use `?` on non-option variable', node.pos)
|
||||
}
|
||||
unwrapped_typ := info.typ.clear_flag(.option).clear_flag(.result)
|
||||
c.expected_or_type = unwrapped_typ
|
||||
c.stmts_ending_with_expression(node.or_expr.stmts)
|
||||
c.check_or_expr(node.or_expr, info.typ, c.expected_or_type, node)
|
||||
return unwrapped_typ
|
||||
}
|
||||
return info.typ
|
||||
} else if node.kind == .function {
|
||||
info := node.info as ast.IdentFn
|
||||
@ -3171,6 +3211,7 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
|
||||
}
|
||||
}
|
||||
is_option := typ.has_flag(.option) || typ.has_flag(.result)
|
||||
|| node.or_expr.kind != .absent
|
||||
node.kind = .variable
|
||||
node.info = ast.IdentVar{
|
||||
typ: typ
|
||||
@ -3182,7 +3223,17 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
|
||||
node.obj = obj
|
||||
// unwrap option (`println(x)`)
|
||||
if is_option {
|
||||
return typ.clear_flag(.option).clear_flag(.result)
|
||||
if node.or_expr.kind == .absent {
|
||||
return typ.clear_flag(.result)
|
||||
}
|
||||
if !typ.has_flag(.option) && node.or_expr.kind == .propagate_option {
|
||||
c.error('cannot use `?` on non-option variable', node.pos)
|
||||
}
|
||||
unwrapped_typ := typ.clear_flag(.option).clear_flag(.result)
|
||||
c.expected_or_type = unwrapped_typ
|
||||
c.stmts_ending_with_expression(node.or_expr.stmts)
|
||||
c.check_or_expr(node.or_expr, typ, c.expected_or_type, node)
|
||||
return unwrapped_typ
|
||||
}
|
||||
return typ
|
||||
}
|
||||
@ -3667,7 +3718,7 @@ fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type {
|
||||
}
|
||||
|
||||
if node.right.typ.has_flag(.option) {
|
||||
c.error('cannot take the address of an option field', node.pos.extend(node.right.pos))
|
||||
c.error('cannot take the address of an Option field', node.pos.extend(node.right.pos))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3843,8 +3894,13 @@ fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type {
|
||||
c.error('type `${typ_sym.name}` does not support indexing', node.pos)
|
||||
}
|
||||
if typ.has_flag(.option) {
|
||||
c.error('type `?${typ_sym.name}` is an option, it does not support indexing',
|
||||
node.left.pos())
|
||||
if node.left is ast.Ident && (node.left as ast.Ident).or_expr.kind == .absent {
|
||||
c.error('type `?${typ_sym.name}` is an Option, it must be unwrapped first; use `var?[]` to do it',
|
||||
node.left.pos())
|
||||
} else if node.left is ast.CallExpr {
|
||||
c.error('type `?${typ_sym.name}` is an Option, it must be unwrapped with `func()?`, or use `func() or {default}`',
|
||||
node.left.pos())
|
||||
}
|
||||
} else if typ.has_flag(.result) {
|
||||
c.error('type `!${typ_sym.name}` is a result, it does not support indexing', node.left.pos())
|
||||
}
|
||||
|
@ -12,6 +12,10 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type {
|
||||
if node.typ != ast.void_type {
|
||||
if node.elem_type != 0 {
|
||||
elem_sym := c.table.sym(node.elem_type)
|
||||
|
||||
if node.typ.has_flag(.option) && (node.has_cap || node.has_len) {
|
||||
c.error('Option array `${elem_sym.name}` cannot have initializers', node.pos)
|
||||
}
|
||||
if elem_sym.kind == .struct_ {
|
||||
elem_info := elem_sym.info as ast.Struct
|
||||
if elem_info.generic_types.len > 0 && elem_info.concrete_types.len == 0
|
||||
|
@ -117,9 +117,6 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||
if multi_type == ast.error_type {
|
||||
c.error('type `IError` cannot be used in multi-return, return an option instead',
|
||||
node.return_type_pos)
|
||||
} else if multi_type.has_flag(.option) {
|
||||
c.error('option cannot be used in multi-return, return an option instead',
|
||||
node.return_type_pos)
|
||||
} else if multi_type.has_flag(.result) {
|
||||
c.error('result cannot be used in multi-return, return a result instead',
|
||||
node.return_type_pos)
|
||||
@ -211,8 +208,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||
c.error('invalid use of reserved type `${param.name}` as a parameter name',
|
||||
param.pos)
|
||||
}
|
||||
if param.typ.has_flag(.option) || param.typ.has_flag(.result) {
|
||||
c.error('option or result type argument is not supported currently', param.type_pos)
|
||||
if param.typ.has_flag(.result) {
|
||||
c.error('result type argument is not supported currently', param.type_pos)
|
||||
}
|
||||
arg_typ_sym := c.table.sym(param.typ)
|
||||
if arg_typ_sym.info is ast.Struct {
|
||||
@ -503,7 +500,7 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type {
|
||||
node.free_receiver = true
|
||||
}
|
||||
}
|
||||
c.expected_or_type = node.return_type.clear_flag(.option).clear_flag(.result)
|
||||
c.expected_or_type = node.return_type.clear_flag(.result)
|
||||
c.stmts_ending_with_expression(node.or_block.stmts)
|
||||
c.expected_or_type = ast.void_type
|
||||
|
||||
@ -1036,8 +1033,8 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
|
||||
}
|
||||
}
|
||||
arg_typ_sym := c.table.sym(arg_typ)
|
||||
if arg_typ_sym.kind == .none_ {
|
||||
c.error('cannot use `none` as function argument', call_arg.pos)
|
||||
if arg_typ_sym.kind == .none_ && param.typ.has_flag(.generic) {
|
||||
c.error('cannot use `none` as generic argument', call_arg.pos)
|
||||
}
|
||||
param_typ_sym := c.table.sym(param.typ)
|
||||
if func.is_variadic && arg_typ.has_flag(.variadic) && node.args.len - 1 > i {
|
||||
@ -1768,8 +1765,8 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if final_arg_sym.kind == .none_ {
|
||||
c.error('cannot use `none` as method argument', arg.pos)
|
||||
if final_arg_sym.kind == .none_ && param.typ.has_flag(.generic) {
|
||||
c.error('cannot use `none` as generic argument', arg.pos)
|
||||
}
|
||||
if param.typ.is_ptr() && !arg.typ.is_real_pointer() && arg.expr.is_literal()
|
||||
&& !c.pref.translated {
|
||||
|
@ -27,7 +27,7 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
||||
return ast.bool_type
|
||||
}
|
||||
node.right_type = right_type
|
||||
if left_type.is_number() && !left_type.is_ptr()
|
||||
if !left_type.has_flag(.option) && left_type.is_number() && !left_type.is_ptr()
|
||||
&& right_type in [ast.int_literal_type, ast.float_literal_type] {
|
||||
node.right_type = left_type
|
||||
if left_type in [ast.f32_type_idx, ast.f64_type_idx] && right_type == ast.float_literal_type {
|
||||
@ -438,7 +438,8 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if left_type.has_flag(.option) || right_type.has_flag(.option) {
|
||||
} else if node.left !is ast.Ident
|
||||
&& (left_type.has_flag(.option) || right_type.has_flag(.option)) {
|
||||
opt_comp_pos := if left_type.has_flag(.option) { left_pos } else { right_pos }
|
||||
c.error('unwrapped option cannot be compared in an infix expression',
|
||||
opt_comp_pos)
|
||||
@ -668,7 +669,7 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
||||
// TODO move this to symmetric_check? Right now it would break `return 0` for `fn()?int `
|
||||
left_is_option := left_type.has_flag(.option)
|
||||
right_is_option := right_type.has_flag(.option)
|
||||
if left_is_option || right_is_option {
|
||||
if node.left !is ast.Ident && (left_is_option || right_is_option) {
|
||||
opt_infix_pos := if left_is_option { left_pos } else { right_pos }
|
||||
c.error('unwrapped option cannot be used in an infix expression', opt_infix_pos)
|
||||
}
|
||||
@ -694,6 +695,9 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
|
||||
node.promoted_type = return_type
|
||||
return return_type
|
||||
}
|
||||
if node.right is ast.None && left_is_option {
|
||||
return ast.bool_type
|
||||
}
|
||||
c.error('infix expr: cannot use `${right_sym.name}` (right expression) as `${left_sym.name}`',
|
||||
left_right_pos)
|
||||
} else if left_type.is_ptr() {
|
||||
|
@ -125,9 +125,17 @@ fn (mut c Checker) return_stmt(mut node ast.Return) {
|
||||
return
|
||||
}
|
||||
for i, exp_type in expected_types {
|
||||
exprv := node.exprs[expr_idxs[i]]
|
||||
if exprv is ast.Ident && (exprv as ast.Ident).or_expr.kind == .propagate_option {
|
||||
if exp_type.has_flag(.option) {
|
||||
c.warn('unwrapping option is redundant as the function returns option',
|
||||
node.pos)
|
||||
} else {
|
||||
c.error('should not unwrap option var on return, it could be none', node.pos)
|
||||
}
|
||||
}
|
||||
got_typ := c.unwrap_generic(got_types[i])
|
||||
if got_typ.has_flag(.option) && (!exp_type.has_flag(.option)
|
||||
|| c.table.type_to_str(got_typ) != c.table.type_to_str(exp_type)) {
|
||||
if got_typ.has_flag(.option) && got_typ.clear_flag(.option) != exp_type.clear_flag(.option) {
|
||||
pos := node.exprs[expr_idxs[i]].pos()
|
||||
c.error('cannot use `${c.table.type_to_str(got_typ)}` as type `${c.table.type_to_str(exp_type)}` in return argument',
|
||||
pos)
|
||||
|
@ -1,13 +1,7 @@
|
||||
vlib/v/checker/tests/alias_type_cast_option_result_unhandled_err.vv:15:12: error: ret_abc_result() returns a result, so it should have either an `or {}` block, or `!` at the end
|
||||
13 | }
|
||||
14 |
|
||||
14 |
|
||||
15 | a := Alias(ret_abc_result())
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
16 | b := Alias(ret_abc_option())
|
||||
17 | println('${a}${b}')
|
||||
vlib/v/checker/tests/alias_type_cast_option_result_unhandled_err.vv:16:12: error: ret_abc_option() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
14 |
|
||||
15 | a := Alias(ret_abc_result())
|
||||
16 | b := Alias(ret_abc_option())
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
17 | println('${a}${b}')
|
||||
|
@ -1,6 +0,0 @@
|
||||
vlib/v/checker/tests/aliased_option_fn_call_err.vv:8:10: error: foo() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
6 |
|
||||
7 | fn main() {
|
||||
8 | println(foo())
|
||||
| ~~~~~
|
||||
9 | }
|
@ -1,9 +0,0 @@
|
||||
type OptStr = ?string
|
||||
|
||||
fn foo() OptStr {
|
||||
return 'abc'
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println(foo())
|
||||
}
|
@ -1,11 +1,6 @@
|
||||
vlib/v/checker/tests/as_cast_option_result_unhandled_err.vv:11:6: error: ret_sum_result() returns a result, so it should have either an `or {}` block, or `!` at the end
|
||||
9 | }
|
||||
10 |
|
||||
11 | _ := ret_sum_result() as int
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
12 | _ := ret_sum_option() as string
|
||||
vlib/v/checker/tests/as_cast_option_result_unhandled_err.vv:12:6: error: ret_sum_option() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
10 |
|
||||
11 | _ := ret_sum_result() as int
|
||||
12 | _ := ret_sum_option() as string
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
vlib/v/checker/tests/as_cast_option_result_unhandled_err.vv:11:6: error: ret_sum_result() returns a result, so it should have either an `or {}` block, or `!` at the end
|
||||
9 | }
|
||||
10 |
|
||||
11 | _ := ret_sum_result() as int
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
12 | _ := ret_sum_option() as string
|
||||
|
@ -1,7 +0,0 @@
|
||||
vlib/v/checker/tests/defer_option.vv:5:3: error: opt() returns an option, so it should have an `or {}` block at the end
|
||||
3 | fn thing() ?string {
|
||||
4 | defer {
|
||||
5 | opt()
|
||||
| ~~~~~
|
||||
6 | }
|
||||
7 | return 'ok'
|
@ -1,8 +0,0 @@
|
||||
fn opt() ? {}
|
||||
|
||||
fn thing() ?string {
|
||||
defer {
|
||||
opt()
|
||||
}
|
||||
return 'ok'
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
vlib/v/checker/tests/expression_should_return_an_option.vv:26:10: error: expression should either return an option or a result
|
||||
vlib/v/checker/tests/expression_should_return_an_option.vv:26:10: error: expression should either return an Option or a result
|
||||
24 | return_option(true) or { println(err) }
|
||||
25 | // should be an checker error:
|
||||
26 | if x := return_string() {
|
||||
| ~~~~~~~~~~~~~~~
|
||||
27 | println('x: ${x}')
|
||||
28 | }
|
||||
28 | }
|
||||
|
@ -1,5 +0,0 @@
|
||||
vlib/v/checker/tests/fn_arg_of_option_err.vv:1:17: error: option or result type argument is not supported currently
|
||||
1 | fn option_arg(x ?int) {
|
||||
| ~~~~
|
||||
2 | println('int type: ${x}')
|
||||
3 | }
|
@ -1,7 +0,0 @@
|
||||
fn option_arg(x ?int) {
|
||||
println('int type: ${x}')
|
||||
}
|
||||
|
||||
fn main() {
|
||||
option_arg(1)
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
vlib/v/checker/tests/fn_call_using_none_arg_err.vv:6:4: error: cannot use `none` as function argument
|
||||
4 |
|
||||
vlib/v/checker/tests/fn_call_using_none_arg_err.vv:6:4: error: cannot use `none` as generic argument
|
||||
4 |
|
||||
5 | fn main() {
|
||||
6 | f(none)
|
||||
| ~~~~
|
||||
|
@ -1,7 +1,7 @@
|
||||
vlib/v/checker/tests/fn_return_or_err.vv:6:17: error: unexpected `or` block, the function `pop` does not return an option or a result
|
||||
4 |
|
||||
vlib/v/checker/tests/fn_return_or_err.vv:6:17: error: unexpected `or` block, the function `pop` does not return an Option or a result
|
||||
4 |
|
||||
5 | pub fn next(mut v []Typ) Typ {
|
||||
6 | return v.pop() or { Typ{} }
|
||||
| ~~~~~~~~~~~~
|
||||
7 | }
|
||||
8 |
|
||||
8 |
|
||||
|
@ -1,25 +1,25 @@
|
||||
vlib/v/checker/tests/go_wait_or.vv:11:16: error: unexpected `?`, the function `wait` does not return an option or a result
|
||||
vlib/v/checker/tests/go_wait_or.vv:11:16: error: unexpected `?`, the function `wait` does not return an Option or a result
|
||||
9 | spawn 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 option or a result
|
||||
vlib/v/checker/tests/go_wait_or.vv:13:20: error: unexpected `or` block, the function `wait` does not return an Option or 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 option or a result
|
||||
vlib/v/checker/tests/go_wait_or.vv:19:13: error: unexpected `or` block, the function `wait` does not return an Option or a result
|
||||
17 | spawn e(1)
|
||||
18 | ]
|
||||
19 | tg2.wait() or { panic('problem') }
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
20 | tg2[0].wait()?
|
||||
21 | tg3 := [
|
||||
vlib/v/checker/tests/go_wait_or.vv:20:15: error: unexpected `?`, the function `wait` does not return an option or a result
|
||||
vlib/v/checker/tests/go_wait_or.vv:20:15: error: unexpected `?`, the function `wait` does not return an Option or a result
|
||||
18 | ]
|
||||
19 | tg2.wait() or { panic('problem') }
|
||||
20 | tg2[0].wait()?
|
||||
@ -33,20 +33,6 @@ vlib/v/checker/tests/go_wait_or.vv:25:6: error: `.wait()` cannot be called for a
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
26 | for t in tg3 {
|
||||
27 | a := t.wait()
|
||||
vlib/v/checker/tests/go_wait_or.vv:27:10: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
25 | tg3.wait() or { panic('problem') }
|
||||
26 | for t in tg3 {
|
||||
27 | a := t.wait()
|
||||
| ~~~~~~
|
||||
28 | println(a)
|
||||
29 | }
|
||||
vlib/v/checker/tests/go_wait_or.vv:31:15: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
29 | }
|
||||
30 | for i, _ in tg3 {
|
||||
31 | a := tg3[i].wait()
|
||||
| ~~~~~~
|
||||
32 | println(a)
|
||||
33 | }
|
||||
vlib/v/checker/tests/go_wait_or.vv:38:6: error: `.wait()` cannot be called for an array when thread functions return options. Iterate over the arrays elements instead and handle each returned option with `or`.
|
||||
36 | spawn g(1)
|
||||
37 | ]
|
||||
@ -54,16 +40,9 @@ vlib/v/checker/tests/go_wait_or.vv:38:6: error: `.wait()` cannot be called for a
|
||||
| ~~~~~~
|
||||
39 | tg4[0].wait()
|
||||
40 | spawn g(3) or { panic('problem') }
|
||||
vlib/v/checker/tests/go_wait_or.vv:39:9: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
37 | ]
|
||||
38 | tg4.wait()
|
||||
39 | tg4[0].wait()
|
||||
| ~~~~~~
|
||||
40 | spawn g(3) or { panic('problem') }
|
||||
41 | }
|
||||
vlib/v/checker/tests/go_wait_or.vv:40:13: error: option handling cannot be done in `spawn` call. Do it when calling `.wait()`
|
||||
38 | tg4.wait()
|
||||
39 | tg4[0].wait()
|
||||
40 | spawn g(3) or { panic('problem') }
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~
|
||||
41 | }
|
||||
41 | }
|
||||
|
@ -3,10 +3,3 @@ vlib/v/checker/tests/ierror_in_return_tuple.vv:1:29: error: type `IError` cannot
|
||||
| ~~~~~~~~~~~~~~
|
||||
2 | return false, error('')
|
||||
3 | }
|
||||
vlib/v/checker/tests/ierror_in_return_tuple.vv:5:29: error: option cannot be used in multi-return, return an option instead
|
||||
3 | }
|
||||
4 |
|
||||
5 | fn return_option_in_tuple() (bool, ?bool) {
|
||||
| ~~~~~~~~~~~~~
|
||||
6 | return false, error('')
|
||||
7 | }
|
||||
|
@ -1,7 +1,7 @@
|
||||
vlib/v/checker/tests/index_of_option_err.vv:6:7: error: type `?[]int` is an option, it does not support indexing
|
||||
4 |
|
||||
vlib/v/checker/tests/index_of_option_err.vv:6:7: error: type `?[]int` is an Option, it must be unwrapped with `func()?`, or use `func() or {default}`
|
||||
4 |
|
||||
5 | fn main() {
|
||||
6 | a := abc()[0] or { 5 }
|
||||
| ~~~~~
|
||||
7 | dump(a)
|
||||
8 | }
|
||||
8 | }
|
||||
|
@ -1,13 +1,13 @@
|
||||
vlib/v/checker/tests/option_fields_addr_err.vv:9:10: error: cannot take the address of an option field
|
||||
vlib/v/checker/tests/option_fields_addr_err.vv:9:10: error: cannot take the address of an Option field
|
||||
7 | fn main() {
|
||||
8 | x := Wrapper{}
|
||||
9 | if _ := &x.value {
|
||||
| ~~~~~~~~
|
||||
10 | }
|
||||
11 |
|
||||
vlib/v/checker/tests/option_fields_addr_err.vv:12:7: error: cannot take the address of an option field
|
||||
vlib/v/checker/tests/option_fields_addr_err.vv:12:7: error: cannot take the address of an Option field
|
||||
10 | }
|
||||
11 |
|
||||
11 |
|
||||
12 | _ := &x.value
|
||||
| ~~~~~~~~
|
||||
13 | }
|
||||
13 | }
|
||||
|
@ -1,101 +1,10 @@
|
||||
vlib/v/checker/tests/option_fn_err.vv:12:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
10 |
|
||||
11 | const (
|
||||
12 | const_value = bar(0)
|
||||
| ~~~~~~
|
||||
13 | )
|
||||
14 |
|
||||
vlib/v/checker/tests/option_fn_err.vv:18:15: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
16 | f fn (int)
|
||||
17 | mut:
|
||||
18 | value int = bar(0)
|
||||
| ~~~~~~
|
||||
19 | opt ?int = bar(0)
|
||||
20 | }
|
||||
vlib/v/checker/tests/option_fn_err.vv:32:2: error: foo() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
30 | fn main() {
|
||||
31 | // call fn
|
||||
32 | foo()
|
||||
| ~~~~~
|
||||
33 | _ := bar(0)
|
||||
34 | println(twice(bar(0)))
|
||||
vlib/v/checker/tests/option_fn_err.vv:33:7: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
31 | // call fn
|
||||
32 | foo()
|
||||
33 | _ := bar(0)
|
||||
| ~~~~~~
|
||||
34 | println(twice(bar(0)))
|
||||
35 |
|
||||
vlib/v/checker/tests/option_fn_err.vv:34:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
32 | foo()
|
||||
33 | _ := bar(0)
|
||||
34 | println(twice(bar(0)))
|
||||
| ~~~~~~
|
||||
35 |
|
||||
36 | // anon fn
|
||||
vlib/v/checker/tests/option_fn_err.vv:37:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
35 |
|
||||
36 | // anon fn
|
||||
37 | fn (_ int) {}(bar(0))
|
||||
| ~~~~~~
|
||||
38 |
|
||||
39 | // assert
|
||||
vlib/v/checker/tests/option_fn_err.vv:40:9: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
38 |
|
||||
vlib/v/checker/tests/option_fn_err.vv:40:9: error: assert can be used only with `bool` expressions, but found `bool` instead
|
||||
38 |
|
||||
39 | // assert
|
||||
40 | assert bar(true)
|
||||
| ~~~~~~~~~
|
||||
41 |
|
||||
41 |
|
||||
42 | // struct
|
||||
vlib/v/checker/tests/option_fn_err.vv:45:10: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
43 | mut v := Data{
|
||||
44 | f: fn (_ int) {}
|
||||
45 | value: bar(0)
|
||||
| ~~~~~~
|
||||
46 | opt: bar(0)
|
||||
47 | }
|
||||
vlib/v/checker/tests/option_fn_err.vv:48:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
46 | opt: bar(0)
|
||||
47 | }
|
||||
48 | v.add(bar(0)) // call method
|
||||
| ~~~~~~
|
||||
49 | v.f(bar(0)) // call fn field
|
||||
50 |
|
||||
vlib/v/checker/tests/option_fn_err.vv:49:6: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
47 | }
|
||||
48 | v.add(bar(0)) // call method
|
||||
49 | v.f(bar(0)) // call fn field
|
||||
| ~~~~~~
|
||||
50 |
|
||||
51 | // array
|
||||
vlib/v/checker/tests/option_fn_err.vv:53:9: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
51 | // array
|
||||
52 | mut arr := [1, 2]
|
||||
53 | arr << bar(0)
|
||||
| ~~~~~~
|
||||
54 | // init
|
||||
55 | _ := [bar(0)]
|
||||
vlib/v/checker/tests/option_fn_err.vv:55:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
53 | arr << bar(0)
|
||||
54 | // init
|
||||
55 | _ := [bar(0)]
|
||||
| ~~~~~~
|
||||
56 | _ := []int{len: 1, init: bar(0)}
|
||||
57 | _ := [bar(0)]!
|
||||
vlib/v/checker/tests/option_fn_err.vv:56:27: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
54 | // init
|
||||
55 | _ := [bar(0)]
|
||||
56 | _ := []int{len: 1, init: bar(0)}
|
||||
| ~~~~~~
|
||||
57 | _ := [bar(0)]!
|
||||
58 | _ := [1]int{init: bar(0)}
|
||||
vlib/v/checker/tests/option_fn_err.vv:57:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
55 | _ := [bar(0)]
|
||||
56 | _ := []int{len: 1, init: bar(0)}
|
||||
57 | _ := [bar(0)]!
|
||||
| ~~~~~~
|
||||
58 | _ := [1]int{init: bar(0)}
|
||||
59 | // index
|
||||
vlib/v/checker/tests/option_fn_err.vv:60:13: error: cannot use option or result as index (array type `[]int`)
|
||||
58 | _ := [1]int{init: bar(0)}
|
||||
59 | // index
|
||||
@ -103,66 +12,24 @@ vlib/v/checker/tests/option_fn_err.vv:60:13: error: cannot use option or result
|
||||
| ~~~~~~~~
|
||||
61 | // array builtin methods
|
||||
62 | arr.insert(0, bar(0))
|
||||
vlib/v/checker/tests/option_fn_err.vv:62:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
60 | println(arr[bar(0)])
|
||||
61 | // array builtin methods
|
||||
62 | arr.insert(0, bar(0))
|
||||
| ~~~~~~
|
||||
63 | arr.prepend(bar(0))
|
||||
64 | arr.contains(bar(0))
|
||||
vlib/v/checker/tests/option_fn_err.vv:63:14: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
61 | // array builtin methods
|
||||
62 | arr.insert(0, bar(0))
|
||||
63 | arr.prepend(bar(0))
|
||||
| ~~~~~~
|
||||
64 | arr.contains(bar(0))
|
||||
65 | arr.index(bar(0))
|
||||
vlib/v/checker/tests/option_fn_err.vv:64:15: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
62 | arr.insert(0, bar(0))
|
||||
63 | arr.prepend(bar(0))
|
||||
64 | arr.contains(bar(0))
|
||||
| ~~~~~~
|
||||
65 | arr.index(bar(0))
|
||||
66 | println(arr.map(bar(0)))
|
||||
vlib/v/checker/tests/option_fn_err.vv:65:12: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
63 | arr.prepend(bar(0))
|
||||
64 | arr.contains(bar(0))
|
||||
65 | arr.index(bar(0))
|
||||
| ~~~~~~
|
||||
66 | println(arr.map(bar(0)))
|
||||
67 | println(arr.filter(bar(true)))
|
||||
vlib/v/checker/tests/option_fn_err.vv:66:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
64 | arr.contains(bar(0))
|
||||
65 | arr.index(bar(0))
|
||||
66 | println(arr.map(bar(0)))
|
||||
| ~~~~~~
|
||||
67 | println(arr.filter(bar(true)))
|
||||
68 | println(arr.any(bar(true)))
|
||||
vlib/v/checker/tests/option_fn_err.vv:67:21: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
vlib/v/checker/tests/option_fn_err.vv:67:21: error: type mismatch, `bar` must return a bool
|
||||
65 | arr.index(bar(0))
|
||||
66 | println(arr.map(bar(0)))
|
||||
67 | println(arr.filter(bar(true)))
|
||||
| ~~~~~~~~~
|
||||
68 | println(arr.any(bar(true)))
|
||||
69 | println(arr.all(bar(true)))
|
||||
vlib/v/checker/tests/option_fn_err.vv:68:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
vlib/v/checker/tests/option_fn_err.vv:68:18: error: type mismatch, `bar` must return a bool
|
||||
66 | println(arr.map(bar(0)))
|
||||
67 | println(arr.filter(bar(true)))
|
||||
68 | println(arr.any(bar(true)))
|
||||
| ~~~~~~~~~
|
||||
69 | println(arr.all(bar(true)))
|
||||
70 |
|
||||
vlib/v/checker/tests/option_fn_err.vv:69:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
vlib/v/checker/tests/option_fn_err.vv:69:18: error: type mismatch, `bar` must return a bool
|
||||
67 | println(arr.filter(bar(true)))
|
||||
68 | println(arr.any(bar(true)))
|
||||
69 | println(arr.all(bar(true)))
|
||||
| ~~~~~~~~~
|
||||
70 |
|
||||
70 |
|
||||
71 | match bar(0) {
|
||||
vlib/v/checker/tests/option_fn_err.vv:71:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
69 | println(arr.all(bar(true)))
|
||||
70 |
|
||||
71 | match bar(0) {
|
||||
| ~~~~~~
|
||||
72 | 0 {}
|
||||
73 | else {}
|
@ -1,6 +0,0 @@
|
||||
vlib/v/checker/tests/option_in_dump_err.vv:10:7: error: create() returns an option, so it should have either an `or {}` block, or `?` at the end
|
||||
8 |
|
||||
9 | fn main() {
|
||||
10 | dump(create())
|
||||
| ~~~~~~~~
|
||||
11 | }
|
@ -1,11 +0,0 @@
|
||||
struct AStruct {
|
||||
field1 int
|
||||
}
|
||||
|
||||
fn create() ?AStruct {
|
||||
return AStruct{123}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
dump(create())
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
vlib/v/checker/tests/option_or_block_none_err.vv:19:32: error: wrong return type `none` in the `or {}` block, expected `Animal`
|
||||
17 |
|
||||
18 | fn main() {
|
||||
19 | mut dog := new_animal(9) or { none }
|
||||
| ~~~~
|
||||
20 |
|
||||
21 | println(dog)
|
@ -1,22 +0,0 @@
|
||||
module main
|
||||
|
||||
struct Animal {
|
||||
mut:
|
||||
height u8
|
||||
}
|
||||
|
||||
fn new_animal(height u8) ?Animal {
|
||||
if height < 10 {
|
||||
return error('Too small to be an animal!')
|
||||
}
|
||||
|
||||
return Animal{
|
||||
height: height
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut dog := new_animal(9) or { none }
|
||||
|
||||
println(dog)
|
||||
}
|
14
vlib/v/checker/tests/option_var_unwrap_err.out
Normal file
14
vlib/v/checker/tests/option_var_unwrap_err.out
Normal file
@ -0,0 +1,14 @@
|
||||
vlib/v/checker/tests/option_var_unwrap_err.vv:5:9: error: to propagate the Option, `abc_2` must return an Option type
|
||||
3 |
|
||||
4 | fn abc_2(a ?string) string {
|
||||
5 | return a?
|
||||
| ^
|
||||
6 | }
|
||||
7 |
|
||||
Details: vlib/v/checker/tests/option_var_unwrap_err.vv:4:21: details: prepend ? before the declaration of the return type of `abc_2`
|
||||
2 | }
|
||||
3 |
|
||||
4 | fn abc_2(a ?string) string {
|
||||
| ~~~~~~
|
||||
5 | return a?
|
||||
6 | }
|
10
vlib/v/checker/tests/option_var_unwrap_err.vv
Normal file
10
vlib/v/checker/tests/option_var_unwrap_err.vv
Normal file
@ -0,0 +1,10 @@
|
||||
fn abc_1() {
|
||||
}
|
||||
|
||||
fn abc_2(a ?string) string {
|
||||
return a?
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
abc_2(none)
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
vlib/v/checker/tests/option_variable_err.vv:2:7: error: casting to option type is forbidden
|
||||
1 | fn main() {
|
||||
2 | _ := ?bool(false)
|
||||
| ~~~~~~~~~~~~
|
||||
3 | }
|
@ -1,3 +0,0 @@
|
||||
fn main() {
|
||||
_ := ?bool(false)
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
vlib/v/checker/tests/or_expr_types_mismatch.vv:3:19: error: wrong return type `none` in the `or {}` block, expected `string`
|
||||
1 | fn get_map() ?string {
|
||||
2 | m := {1: 'a', 2: 'b'}
|
||||
3 | return m[1] or { none }
|
||||
| ~~~~
|
||||
4 | }
|
||||
5 |
|
||||
vlib/v/checker/tests/or_expr_types_mismatch.vv:8:19: error: wrong return type `none` in the `or {}` block, expected `int`
|
||||
6 | fn get_array() ?int {
|
||||
7 | a := [1, 2, 3]
|
||||
8 | return a[4] or { none }
|
||||
| ~~~~
|
||||
9 | }
|
||||
10 |
|
@ -1,17 +0,0 @@
|
||||
fn get_map() ?string {
|
||||
m := {1: 'a', 2: 'b'}
|
||||
return m[1] or { none }
|
||||
}
|
||||
|
||||
fn get_array() ?int {
|
||||
a := [1, 2, 3]
|
||||
return a[4] or { none }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
map_result := get_map() or { return }
|
||||
println(map_result)
|
||||
|
||||
array_result := get_array() or { return }
|
||||
println(array_result)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
vlib/v/checker/tests/propagate_option_with_result_err.vv:6:7: warning: propagating a result like an option is deprecated, use `foo()!` instead of `foo()?`
|
||||
vlib/v/checker/tests/propagate_option_with_result_err.vv:6:7: warning: propagating a result like an Option is deprecated, use `foo()!` instead of `foo()?`
|
||||
4 |
|
||||
5 | fn bar() ?string {
|
||||
6 | foo()?
|
||||
|
@ -1,6 +1,6 @@
|
||||
vlib/v/checker/tests/selector_expr_option_err.vv:10:16: error: cannot access fields of an option, handle the error with `or {...}` or propagate it with `?`
|
||||
8 |
|
||||
vlib/v/checker/tests/selector_expr_option_err.vv:10:16: error: cannot access fields of an Option, handle the error with `or {...}` or propagate it with `?`
|
||||
8 |
|
||||
9 | fn main() {
|
||||
10 | println(abc().status_code)
|
||||
| ~~~~~~~~~~~
|
||||
11 | }
|
||||
11 | }
|
||||
|
@ -5,37 +5,30 @@ vlib/v/checker/tests/struct_field_option_err.vv:3:6: error: struct field does no
|
||||
| ^
|
||||
4 | bar ?int = 1
|
||||
5 | baz int = 1
|
||||
vlib/v/checker/tests/struct_field_option_err.vv:11:8: error: field `bar` is an option, so it should have either an `or {}` block, or `?` at the end
|
||||
9 | mut f := Foo{}
|
||||
10 |
|
||||
11 | _ = f.bar
|
||||
| ~~~
|
||||
12 | _ = f.bar + 1
|
||||
13 |
|
||||
vlib/v/checker/tests/struct_field_option_err.vv:12:12: error: `+` cannot be used with `?int`
|
||||
10 |
|
||||
10 |
|
||||
11 | _ = f.bar
|
||||
12 | _ = f.bar + 1
|
||||
| ^
|
||||
13 |
|
||||
13 |
|
||||
14 | _ = f.bar!
|
||||
vlib/v/checker/tests/struct_field_option_err.vv:14:11: error: to propagate a result, the call must also return a result type
|
||||
12 | _ = f.bar + 1
|
||||
13 |
|
||||
13 |
|
||||
14 | _ = f.bar!
|
||||
| ^
|
||||
15 |
|
||||
15 |
|
||||
16 | _ = f.bar or { _ = 1 }
|
||||
vlib/v/checker/tests/struct_field_option_err.vv:16:19: error: last statement in the `or {}` block should be an expression of type `int` or exit parent scope
|
||||
14 | _ = f.bar!
|
||||
15 |
|
||||
15 |
|
||||
16 | _ = f.bar or { _ = 1 }
|
||||
| ^
|
||||
17 |
|
||||
17 |
|
||||
18 | _ = f.baz?
|
||||
vlib/v/checker/tests/struct_field_option_err.vv:18:11: error: unexpected `?`, the field `baz` is neither an option, nor a result
|
||||
vlib/v/checker/tests/struct_field_option_err.vv:18:11: error: unexpected `?`, the field `baz` is neither an Option, nor a result
|
||||
16 | _ = f.bar or { _ = 1 }
|
||||
17 |
|
||||
17 |
|
||||
18 | _ = f.baz?
|
||||
| ^
|
||||
19 | }
|
||||
19 | }
|
||||
|
@ -1,5 +1,5 @@
|
||||
vlib/v/checker/tests/type_cast_option_err.vv:2:10: error: cannot type cast an option
|
||||
vlib/v/checker/tests/type_cast_option_err.vv:2:10: error: cannot type cast an Option
|
||||
1 | fn main() {
|
||||
2 | println(int('hi'.last_index('i')))
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
3 | }
|
||||
3 | }
|
||||
|
@ -1,6 +1,6 @@
|
||||
vlib/v/checker/tests/unexpected_or.vv:6:17: error: unexpected `or` block, the function `ret_zero` does not return an option or a result
|
||||
4 |
|
||||
vlib/v/checker/tests/unexpected_or.vv:6:17: error: unexpected `or` block, the function `ret_zero` does not return an Option or a result
|
||||
4 |
|
||||
5 | fn main() {
|
||||
6 | _ = ret_zero() or { 1 }
|
||||
| ~~~~~~~~
|
||||
7 | }
|
||||
7 | }
|
||||
|
@ -1,7 +1,7 @@
|
||||
vlib/v/checker/tests/unexpected_or_propagate.vv:6:17: error: unexpected `?`, the function `ret_zero` does not return an option or a result
|
||||
4 |
|
||||
vlib/v/checker/tests/unexpected_or_propagate.vv:6:17: error: unexpected `?`, the function `ret_zero` does not return an Option or a result
|
||||
4 |
|
||||
5 | fn opt_fn() ?int {
|
||||
6 | a := ret_zero()?
|
||||
| ^
|
||||
7 | return a
|
||||
8 | }
|
||||
8 | }
|
||||
|
6
vlib/v/checker/tests/var_option_index_error.out
Normal file
6
vlib/v/checker/tests/var_option_index_error.out
Normal file
@ -0,0 +1,6 @@
|
||||
vlib/v/checker/tests/var_option_index_error.vv:4:2: error: type `?map[string]string` is an Option, it must be unwrapped first; use `var?[]` to do it
|
||||
2 | mut var4 := ?map[string]string{}
|
||||
3 | var4?['a'] = 'b'
|
||||
4 | var4['b'] = 'c'
|
||||
| ~~~~
|
||||
5 | }
|
5
vlib/v/checker/tests/var_option_index_error.vv
Normal file
5
vlib/v/checker/tests/var_option_index_error.vv
Normal file
@ -0,0 +1,5 @@
|
||||
fn main() {
|
||||
mut var4 := ?map[string]string{}
|
||||
var4?['a'] = 'b'
|
||||
var4['b'] = 'c'
|
||||
}
|
7
vlib/v/checker/tests/var_option_wrong_default.out
Normal file
7
vlib/v/checker/tests/var_option_wrong_default.out
Normal file
@ -0,0 +1,7 @@
|
||||
vlib/v/checker/tests/var_option_wrong_default.vv:4:19: error: `or` block must provide a value of type `int`, not `?int`
|
||||
2 |
|
||||
3 | var2 := var1 or { var1? }
|
||||
4 | var3 := var1 or { var1 }
|
||||
| ~~~~
|
||||
5 |
|
||||
6 | println(var2)
|
7
vlib/v/checker/tests/var_option_wrong_default.vv
Normal file
7
vlib/v/checker/tests/var_option_wrong_default.vv
Normal file
@ -0,0 +1,7 @@
|
||||
var1 := ?int(none)
|
||||
|
||||
var2 := var1 or { var1? }
|
||||
var3 := var1 or { var1 }
|
||||
|
||||
println(var2)
|
||||
println(var3)
|
6
vlib/v/checker/tests/var_option_wrong_type.out
Normal file
6
vlib/v/checker/tests/var_option_wrong_type.out
Normal file
@ -0,0 +1,6 @@
|
||||
vlib/v/checker/tests/var_option_wrong_type.vv:3:23: error: mismatched types `?f64` and `string`
|
||||
1 | var_none := ?f64(none)
|
||||
2 |
|
||||
3 | var3 := var_none or { var_none + 'foo' }
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
4 | println(var3)
|
4
vlib/v/checker/tests/var_option_wrong_type.vv
Normal file
4
vlib/v/checker/tests/var_option_wrong_type.vv
Normal file
@ -0,0 +1,4 @@
|
||||
var_none := ?f64(none)
|
||||
|
||||
var3 := var_none or { var_none + 'foo' }
|
||||
println(var3)
|
5
vlib/v/checker/tests/var_option_wrong_unwrap.out
Normal file
5
vlib/v/checker/tests/var_option_wrong_unwrap.out
Normal file
@ -0,0 +1,5 @@
|
||||
vlib/v/checker/tests/var_option_wrong_unwrap.vv:3:9: error: cannot use `?` on non-option variable
|
||||
1 | var1 := ?int(none)
|
||||
2 | var2 := var1 or { var1? }
|
||||
3 | println(var2?)
|
||||
| ~~~~
|
3
vlib/v/checker/tests/var_option_wrong_unwrap.vv
Normal file
3
vlib/v/checker/tests/var_option_wrong_unwrap.vv
Normal file
@ -0,0 +1,3 @@
|
||||
var1 := ?int(none)
|
||||
var2 := var1 or { var1? }
|
||||
println(var2?)
|
@ -2006,6 +2006,11 @@ pub fn (mut f Fmt) ident(node ast.Ident) {
|
||||
}
|
||||
name := f.short_module(node.name)
|
||||
f.write(name)
|
||||
if node.or_expr.kind == .propagate_option {
|
||||
f.write('?')
|
||||
} else if node.or_expr.kind == .block {
|
||||
f.or_expr(node.or_expr)
|
||||
}
|
||||
f.mark_import_as_used(name)
|
||||
}
|
||||
}
|
||||
|
@ -247,6 +247,9 @@ pub fn (mut f Fmt) struct_init(node ast.StructInit) {
|
||||
if name == 'void' {
|
||||
name = ''
|
||||
}
|
||||
if node.typ.has_flag(.option) {
|
||||
f.write('?')
|
||||
}
|
||||
if node.is_anon {
|
||||
f.write('struct ')
|
||||
}
|
||||
|
@ -7,6 +7,97 @@ import v.ast
|
||||
import v.util
|
||||
import v.token
|
||||
|
||||
fn (mut g Gen) expr_with_opt_or_block(expr ast.Expr, expr_typ ast.Type, var_expr ast.Expr, ret_typ ast.Type) {
|
||||
gen_or := expr is ast.Ident && (expr as ast.Ident).or_expr.kind != .absent
|
||||
if gen_or {
|
||||
old_inside_opt_data := g.inside_opt_data
|
||||
g.inside_opt_data = true
|
||||
g.expr_with_cast(expr, expr_typ, ret_typ)
|
||||
g.writeln(';')
|
||||
g.writeln('if (${expr}.state != 0) {')
|
||||
if expr is ast.Ident && (expr as ast.Ident).or_expr.kind == .propagate_option {
|
||||
g.write('return ')
|
||||
g.gen_option_error(g.cur_fn.return_type, expr)
|
||||
g.writeln(';')
|
||||
} else {
|
||||
g.gen_or_block_stmts(var_expr.str(), '', (expr as ast.Ident).or_expr.stmts,
|
||||
ret_typ, false)
|
||||
}
|
||||
g.writeln('}')
|
||||
g.inside_opt_data = old_inside_opt_data
|
||||
} else {
|
||||
g.expr_with_opt(expr, expr_typ, ret_typ)
|
||||
}
|
||||
}
|
||||
|
||||
// expr_opt_with_cast is used in cast expr when converting compatible option types
|
||||
// e.g. ?int(?u8(0))
|
||||
fn (mut g Gen) expr_opt_with_cast(expr ast.Expr, expr_typ ast.Type, ret_typ ast.Type) string {
|
||||
if !expr_typ.has_flag(.option) || !ret_typ.has_flag(.option) {
|
||||
panic('cgen: expected expr_type and ret_typ to be options')
|
||||
}
|
||||
|
||||
if expr_typ.idx() == ret_typ.idx() && g.table.sym(expr_typ).kind != .alias {
|
||||
return g.expr_with_opt(expr, expr_typ, ret_typ)
|
||||
} else {
|
||||
stmt_str := g.go_before_stmt(0).trim_space()
|
||||
styp := g.base_type(ret_typ)
|
||||
g.empty_line = true
|
||||
tmp_var := g.new_tmp_var()
|
||||
g.writeln('${g.typ(ret_typ)} ${tmp_var};')
|
||||
g.write('_option_ok(&(${styp}[]) {')
|
||||
|
||||
if expr is ast.CastExpr && expr_typ.has_flag(.option) {
|
||||
g.write('*((${g.base_type(expr_typ)}*)')
|
||||
g.expr(expr)
|
||||
g.write('.data)')
|
||||
} else {
|
||||
g.inside_opt_or_res = false
|
||||
g.expr(expr)
|
||||
}
|
||||
g.writeln(' }, (${option_name}*)(&${tmp_var}), sizeof(${styp}));')
|
||||
g.write(stmt_str)
|
||||
g.write(tmp_var)
|
||||
return tmp_var
|
||||
}
|
||||
}
|
||||
|
||||
// expr_with_opt is used in assigning an expression to an `option` variable
|
||||
// e.g. x = y (option lhs and rhs), mut x = ?int(123), y = none
|
||||
fn (mut g Gen) expr_with_opt(expr ast.Expr, expr_typ ast.Type, ret_typ ast.Type) string {
|
||||
if expr_typ == ast.none_type {
|
||||
old_inside_opt_data := g.inside_opt_data
|
||||
g.inside_opt_or_res = true
|
||||
defer {
|
||||
g.inside_opt_data = old_inside_opt_data
|
||||
}
|
||||
}
|
||||
if expr_typ.has_flag(.option) && ret_typ.has_flag(.option)&& (expr in [ast.Ident, ast.ComptimeSelector, ast.AsCast, ast.CallExpr, ast.MatchExpr, ast.IfExpr, ast.IndexExpr, ast.UnsafeExpr, ast.CastExpr]) {
|
||||
if expr in [ast.Ident, ast.CastExpr] {
|
||||
if expr_typ.idx() != ret_typ.idx() {
|
||||
return g.expr_opt_with_cast(expr, expr_typ, ret_typ)
|
||||
}
|
||||
old_inside_opt_data := g.inside_opt_data
|
||||
g.inside_opt_or_res = true
|
||||
defer {
|
||||
g.inside_opt_data = old_inside_opt_data
|
||||
}
|
||||
}
|
||||
g.expr(expr)
|
||||
return expr.str()
|
||||
} else {
|
||||
old_inside_opt_data := g.inside_opt_data
|
||||
g.inside_opt_or_res = true
|
||||
defer {
|
||||
g.inside_opt_data = old_inside_opt_data
|
||||
}
|
||||
tmp_out_var := g.new_tmp_var()
|
||||
g.expr_with_tmp_var(expr, expr_typ, ret_typ, tmp_out_var)
|
||||
return tmp_out_var
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
mut node := unsafe { node_ }
|
||||
if node.is_static {
|
||||
@ -98,8 +189,8 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
mut val_type := node.right_types[i]
|
||||
val := node.right[i]
|
||||
mut is_call := false
|
||||
mut gen_or := false
|
||||
mut blank_assign := false
|
||||
mut is_comptime_var := false
|
||||
mut ident := ast.Ident{
|
||||
scope: 0
|
||||
}
|
||||
@ -125,21 +216,30 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
&& (val as ast.Ident).info is ast.IdentVar && (val as ast.Ident).kind == .variable && (val as ast.Ident).obj is ast.Var && ((val as ast.Ident).obj as ast.Var).is_comptime_field {
|
||||
var_type = g.unwrap_generic(g.comptime_for_field_type)
|
||||
val_type = var_type
|
||||
gen_or = val.or_expr.kind != .absent
|
||||
if gen_or {
|
||||
var_type = val_type.clear_flag(.option)
|
||||
}
|
||||
left.obj.typ = var_type
|
||||
is_comptime_var = true
|
||||
} else if val is ast.ComptimeSelector {
|
||||
key_str := g.get_comptime_selector_key_type(val)
|
||||
if key_str != '' {
|
||||
var_type = g.comptime_var_type_map[key_str] or { var_type }
|
||||
val_type = var_type
|
||||
left.obj.typ = var_type
|
||||
is_comptime_var = true
|
||||
val_type = var_type
|
||||
}
|
||||
} else if val is ast.ComptimeCall {
|
||||
key_str := '${val.method_name}.return_type'
|
||||
var_type = g.comptime_var_type_map[key_str] or { var_type }
|
||||
left.obj.typ = var_type
|
||||
is_comptime_var = true
|
||||
} else if is_decl && val is ast.Ident && (val as ast.Ident).info is ast.IdentVar {
|
||||
val_info := (val as ast.Ident).info
|
||||
gen_or = val.or_expr.kind != .absent
|
||||
if val_info.is_option && gen_or {
|
||||
var_type = val_type.clear_flag(.option)
|
||||
left.obj.typ = var_type
|
||||
}
|
||||
}
|
||||
is_auto_heap = left.obj.is_auto_heap
|
||||
}
|
||||
@ -218,9 +318,14 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
&& !g.pref.translated
|
||||
g.is_assign_lhs = true
|
||||
g.assign_op = node.op
|
||||
if val_type.has_flag(.option) || val_type.has_flag(.result) {
|
||||
g.right_is_opt = true
|
||||
|
||||
g.left_is_opt = var_type.has_flag(.option) || var_type.has_flag(.result)
|
||||
g.right_is_opt = val_type.has_flag(.option) || val_type.has_flag(.result)
|
||||
defer {
|
||||
g.left_is_opt = false
|
||||
g.right_is_opt = false
|
||||
}
|
||||
|
||||
if blank_assign {
|
||||
if val is ast.IndexExpr {
|
||||
g.assign_op = .decl_assign
|
||||
@ -233,6 +338,8 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
g.is_void_expr_stmt = old_is_void_expr_stmt
|
||||
} else if g.inside_for_c_stmt {
|
||||
g.expr(val)
|
||||
} else if var_type.has_flag(.option) {
|
||||
g.expr_with_opt(val, val_type, var_type)
|
||||
} else {
|
||||
if left_sym.kind == .function {
|
||||
g.write('{void* _ = ')
|
||||
@ -441,28 +548,11 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
cloned = true
|
||||
}
|
||||
}
|
||||
unwrap_option := !var_type.has_flag(.option) && val_type.has_flag(.option)
|
||||
if unwrap_option {
|
||||
// Unwrap the option now that the testing code has been prepended.
|
||||
// `pos := s.index(...
|
||||
// `int pos = *(int)_t10.data;`
|
||||
// if g.is_autofree {
|
||||
/*
|
||||
if is_option {
|
||||
g.write('*($styp*)')
|
||||
g.write(tmp_opt + '.data/*FFz*/')
|
||||
g.right_is_opt = false
|
||||
if g.inside_ternary == 0 && !node.is_simple {
|
||||
g.writeln(';')
|
||||
}
|
||||
return
|
||||
}
|
||||
*/
|
||||
}
|
||||
if !cloned {
|
||||
if !g.inside_comptime_for_field
|
||||
&& ((var_type.has_flag(.option) && !val_type.has_flag(.option))
|
||||
|| (var_type.has_flag(.result) && !val_type.has_flag(.result))) {
|
||||
g.inside_opt_or_res = true
|
||||
tmp_var := g.new_tmp_var()
|
||||
g.expr_with_tmp_var(val, val_type, var_type, tmp_var)
|
||||
} else if is_fixed_array_var {
|
||||
@ -497,17 +587,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
if val.is_auto_deref_var() {
|
||||
g.write('*')
|
||||
}
|
||||
if val is ast.ArrayInit {
|
||||
if var_type.has_flag(.option) || gen_or {
|
||||
g.expr_with_opt_or_block(val, val_type, left, var_type)
|
||||
} else if val is ast.ArrayInit {
|
||||
g.array_init(val, c_name(ident.name))
|
||||
} else if val_type.has_flag(.shared_f) {
|
||||
g.expr_with_cast(val, val_type, var_type)
|
||||
} else if is_comptime_var && g.right_is_opt {
|
||||
if var_type.has_flag(.option) && val is ast.ComptimeSelector {
|
||||
g.expr(val)
|
||||
} else {
|
||||
tmp_var := g.new_tmp_var()
|
||||
g.expr_with_tmp_var(val, val_type, var_type, tmp_var)
|
||||
}
|
||||
} else {
|
||||
g.expr(val)
|
||||
}
|
||||
@ -516,7 +601,9 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if node.has_cross_var {
|
||||
if var_type.has_flag(.option) || gen_or {
|
||||
g.expr_with_opt_or_block(val, val_type, left, var_type)
|
||||
} else if node.has_cross_var {
|
||||
g.gen_cross_tmp_variable(node.left, val)
|
||||
} else {
|
||||
if op_overloaded {
|
||||
|
@ -64,12 +64,6 @@ fn (mut g Gen) get_str_fn(typ ast.Type) string {
|
||||
unwrapped = ast.u64_type
|
||||
}
|
||||
}
|
||||
if typ.has_flag(.option) {
|
||||
unwrapped.set_flag(.option)
|
||||
}
|
||||
if typ.has_flag(.result) {
|
||||
unwrapped.set_flag(.result)
|
||||
}
|
||||
styp := g.typ(unwrapped)
|
||||
mut sym := g.table.sym(unwrapped)
|
||||
mut str_fn_name := styp_to_str_fn_name(styp)
|
||||
@ -904,9 +898,13 @@ fn (mut g Gen) gen_str_for_struct(info ast.Struct, styp string, typ_str string,
|
||||
sftyp := g.typ(ftyp_noshared)
|
||||
mut field_styp := sftyp.replace('*', '')
|
||||
field_styp_fn_name := if sym_has_str_method {
|
||||
left_cc_type := g.cc_type(ftyp_noshared, false)
|
||||
left_fn_name := util.no_dots(left_cc_type)
|
||||
mut field_fn_name := '${left_fn_name}_str'
|
||||
mut field_fn_name := if ftyp_noshared.has_flag(.option) {
|
||||
'${field_styp}_str'
|
||||
} else {
|
||||
left_cc_type := g.cc_type(ftyp_noshared, false)
|
||||
left_fn_name := util.no_dots(left_cc_type)
|
||||
'${left_fn_name}_str'
|
||||
}
|
||||
if sym.info is ast.Struct {
|
||||
field_fn_name = g.generic_fn_name(sym.info.concrete_types, field_fn_name)
|
||||
}
|
||||
|
@ -147,7 +147,8 @@ mut:
|
||||
arraymap_set_pos int // map or array set value position
|
||||
stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement
|
||||
skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements)
|
||||
right_is_opt bool
|
||||
left_is_opt bool // left hand side on assignment is an option
|
||||
right_is_opt bool // right hand side on assignment is an option
|
||||
indent int
|
||||
empty_line bool
|
||||
assign_op token.Kind // *=, =, etc (for array_set)
|
||||
@ -1842,7 +1843,11 @@ fn (mut g Gen) expr_with_tmp_var(expr ast.Expr, expr_typ ast.Type, ret_typ ast.T
|
||||
} else {
|
||||
g.writeln('${g.typ(ret_typ)} ${tmp_var};')
|
||||
if ret_typ.has_flag(.option) {
|
||||
g.write('_option_ok(&(${styp}[]) { ')
|
||||
if expr_typ.has_flag(.option) && expr in [ast.StructInit, ast.ArrayInit, ast.MapInit] {
|
||||
g.write('_option_none(&(${styp}[]) { ')
|
||||
} else {
|
||||
g.write('_option_ok(&(${styp}[]) { ')
|
||||
}
|
||||
} else {
|
||||
g.write('_result_ok(&(${styp}[]) { ')
|
||||
}
|
||||
@ -4102,7 +4107,7 @@ fn (mut g Gen) ident(node ast.Ident) {
|
||||
if node.obj is ast.Var {
|
||||
if !g.is_assign_lhs && node.obj.is_comptime_field {
|
||||
if g.comptime_for_field_type.has_flag(.option) {
|
||||
if g.inside_opt_or_res {
|
||||
if (g.inside_opt_or_res && node.or_expr.kind == .absent) || g.left_is_opt {
|
||||
g.write('${name}')
|
||||
} else {
|
||||
g.write('/*opt*/')
|
||||
@ -4112,6 +4117,12 @@ fn (mut g Gen) ident(node ast.Ident) {
|
||||
} else {
|
||||
g.write('${name}')
|
||||
}
|
||||
if node.or_expr.kind != .absent {
|
||||
stmt_str := g.go_before_stmt(0).trim_space()
|
||||
g.empty_line = true
|
||||
g.or_block(name, node.or_expr, g.comptime_for_field_type)
|
||||
g.writeln(stmt_str)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -4120,13 +4131,19 @@ fn (mut g Gen) ident(node ast.Ident) {
|
||||
// `x = new_opt()` => `x = new_opt()` (g.right_is_opt == true)
|
||||
// `println(x)` => `println(*(int*)x.data)`
|
||||
if node.info.is_option && !(g.is_assign_lhs && g.right_is_opt) {
|
||||
if g.inside_opt_or_res {
|
||||
if (g.inside_opt_or_res && node.or_expr.kind == .absent) || g.left_is_opt {
|
||||
g.write('${name}')
|
||||
} else {
|
||||
g.write('/*opt*/')
|
||||
styp := g.base_type(node.info.typ)
|
||||
g.write('(*(${styp}*)${name}.data)')
|
||||
}
|
||||
if node.or_expr.kind != .absent {
|
||||
stmt_str := g.go_before_stmt(0).trim_space()
|
||||
g.empty_line = true
|
||||
g.or_block(name, node.or_expr, node.info.typ)
|
||||
g.writeln(stmt_str)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !g.is_assign_lhs && node.info.share == .shared_t {
|
||||
@ -4214,8 +4231,15 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) {
|
||||
expr_type = g.unwrap_generic(g.comptime_for_field_type)
|
||||
}
|
||||
if sym.kind in [.sum_type, .interface_] {
|
||||
g.expr_with_cast(node.expr, expr_type, node_typ)
|
||||
} else if sym.kind == .struct_ && !node.typ.is_ptr() && !(sym.info as ast.Struct).is_typedef {
|
||||
if node.typ.has_flag(.option) && node.expr is ast.None {
|
||||
g.gen_option_error(node.typ, node.expr)
|
||||
} else if node.typ.has_flag(.option) {
|
||||
g.expr_with_opt(node.expr, expr_type, node.typ)
|
||||
} else {
|
||||
g.expr_with_cast(node.expr, expr_type, node_typ)
|
||||
}
|
||||
} else if !node.typ.has_flag(.option) && sym.kind == .struct_ && !node.typ.is_ptr()
|
||||
&& !(sym.info as ast.Struct).is_typedef {
|
||||
// deprecated, replaced by Struct{...exr}
|
||||
styp := g.typ(node.typ)
|
||||
g.write('*((${styp} *)(&')
|
||||
@ -4250,6 +4274,12 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) {
|
||||
}
|
||||
if node.typ.has_flag(.option) && node.expr is ast.None {
|
||||
g.gen_option_error(node.typ, node.expr)
|
||||
} else if node.typ.has_flag(.option) {
|
||||
if sym.kind == .alias && node.expr_type.has_flag(.option) {
|
||||
g.expr_opt_with_cast(node.expr, expr_type, node.typ)
|
||||
} else {
|
||||
g.expr_with_opt(node.expr, expr_type, node.typ)
|
||||
}
|
||||
} else {
|
||||
g.write('(${cast_label}(')
|
||||
if sym.kind == .alias && g.table.final_sym(node.typ).kind == .string {
|
||||
@ -4521,6 +4551,7 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||
fn_return_is_multi := sym.kind == .multi_return
|
||||
fn_return_is_option := fn_ret_type.has_flag(.option)
|
||||
fn_return_is_result := fn_ret_type.has_flag(.result)
|
||||
|
||||
mut has_semicolon := false
|
||||
if node.exprs.len == 0 {
|
||||
g.write_defer_stmts_when_needed()
|
||||
@ -4671,6 +4702,8 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||
}
|
||||
if g.table.sym(mr_info.types[i]).kind in [.sum_type, .interface_] {
|
||||
g.expr_with_cast(expr, node.types[i], mr_info.types[i])
|
||||
} else if mr_info.types[i].has_flag(.option) && !node.types[i].has_flag(.option) {
|
||||
g.expr_with_opt(expr, node.types[i], mr_info.types[i])
|
||||
} else {
|
||||
g.expr(expr)
|
||||
}
|
||||
@ -4811,7 +4844,11 @@ fn (mut g Gen) return_stmt(node ast.Return) {
|
||||
g.expr(expr0)
|
||||
}
|
||||
} else {
|
||||
g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
|
||||
if g.fn_decl.return_type.has_flag(.option) {
|
||||
g.expr_with_opt(node.exprs[0], node.types[0], g.fn_decl.return_type)
|
||||
} else {
|
||||
g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
|
||||
}
|
||||
}
|
||||
if use_tmp_var {
|
||||
g.writeln(';')
|
||||
@ -5727,6 +5764,52 @@ fn (mut g Gen) sort_structs(typesa []&ast.TypeSymbol) []&ast.TypeSymbol {
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_or_block_stmts(cvar_name string, cast_typ string, stmts []ast.Stmt, return_type ast.Type, is_option bool) {
|
||||
g.indent++
|
||||
for i, stmt in stmts {
|
||||
if i == stmts.len - 1 {
|
||||
expr_stmt := stmt as ast.ExprStmt
|
||||
g.set_current_pos_as_last_stmt_pos()
|
||||
if g.inside_return && (expr_stmt.typ.idx() == ast.error_type_idx
|
||||
|| expr_stmt.typ in [ast.none_type, ast.error_type]) {
|
||||
// `return foo() or { error('failed') }`
|
||||
if g.cur_fn != unsafe { nil } {
|
||||
if g.cur_fn.return_type.has_flag(.result) {
|
||||
g.write('return ')
|
||||
g.gen_result_error(g.cur_fn.return_type, expr_stmt.expr)
|
||||
g.writeln(';')
|
||||
} else if g.cur_fn.return_type.has_flag(.option) {
|
||||
g.write('return ')
|
||||
g.gen_option_error(g.cur_fn.return_type, expr_stmt.expr)
|
||||
g.writeln(';')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if expr_stmt.typ == ast.none_type_idx {
|
||||
g.write('${cvar_name} = ')
|
||||
g.gen_option_error(return_type, expr_stmt.expr)
|
||||
g.writeln(';')
|
||||
} else {
|
||||
if is_option {
|
||||
g.write('*(${cast_typ}*) ${cvar_name}.data = ')
|
||||
} else {
|
||||
g.write('${cvar_name} = ')
|
||||
}
|
||||
old_inside_opt_data := g.inside_opt_data
|
||||
g.inside_opt_data = true
|
||||
g.expr_with_cast(expr_stmt.expr, expr_stmt.typ, return_type.clear_flag(.option).clear_flag(.result))
|
||||
g.inside_opt_data = old_inside_opt_data
|
||||
g.writeln(';')
|
||||
g.stmt_path_pos.delete_last()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
g.stmt(stmt)
|
||||
}
|
||||
}
|
||||
g.indent--
|
||||
}
|
||||
|
||||
// fn (mut g Gen) start_tmp() {
|
||||
// }
|
||||
// If user is accessing the return value eg. in assigment, pass the variable name.
|
||||
@ -5768,39 +5851,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Ty
|
||||
stmts := or_block.stmts
|
||||
if stmts.len > 0 && stmts.last() is ast.ExprStmt
|
||||
&& (stmts.last() as ast.ExprStmt).typ != ast.void_type {
|
||||
g.indent++
|
||||
for i, stmt in stmts {
|
||||
if i == stmts.len - 1 {
|
||||
expr_stmt := stmt as ast.ExprStmt
|
||||
g.set_current_pos_as_last_stmt_pos()
|
||||
if g.inside_return && (expr_stmt.typ.idx() == ast.error_type_idx
|
||||
|| expr_stmt.typ in [ast.none_type, ast.error_type]) {
|
||||
// `return foo() or { error('failed') }`
|
||||
if g.cur_fn != unsafe { nil } {
|
||||
if g.cur_fn.return_type.has_flag(.result) {
|
||||
g.write('return ')
|
||||
g.gen_result_error(g.cur_fn.return_type, expr_stmt.expr)
|
||||
g.writeln(';')
|
||||
} else if g.cur_fn.return_type.has_flag(.option) {
|
||||
g.write('return ')
|
||||
g.gen_option_error(g.cur_fn.return_type, expr_stmt.expr)
|
||||
g.writeln(';')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
g.write('*(${mr_styp}*) ${cvar_name}.data = ')
|
||||
old_inside_opt_data := g.inside_opt_data
|
||||
g.inside_opt_data = true
|
||||
g.expr_with_cast(expr_stmt.expr, expr_stmt.typ, return_type.clear_flag(.option).clear_flag(.result))
|
||||
g.inside_opt_data = old_inside_opt_data
|
||||
g.writeln(';')
|
||||
g.stmt_path_pos.delete_last()
|
||||
}
|
||||
} else {
|
||||
g.stmt(stmt)
|
||||
}
|
||||
}
|
||||
g.indent--
|
||||
g.gen_or_block_stmts(cvar_name, mr_styp, stmts, return_type, true)
|
||||
} else {
|
||||
g.stmts(stmts)
|
||||
if stmts.len > 0 && stmts.last() is ast.ExprStmt {
|
||||
|
@ -883,7 +883,11 @@ fn (mut g Gen) gen_to_str_method_call(node ast.CallExpr) bool {
|
||||
}
|
||||
} else if left_node is ast.Ident {
|
||||
if left_node.obj is ast.Var {
|
||||
if g.comptime_var_type_map.len > 0 {
|
||||
if left_node.obj.is_comptime_field {
|
||||
rec_type = g.comptime_for_field_type
|
||||
g.gen_expr_to_string(left_node, rec_type)
|
||||
return true
|
||||
} else if g.comptime_var_type_map.len > 0 {
|
||||
rec_type = left_node.obj.typ
|
||||
g.gen_expr_to_string(left_node, rec_type)
|
||||
return true
|
||||
@ -900,6 +904,9 @@ fn (mut g Gen) gen_to_str_method_call(node ast.CallExpr) bool {
|
||||
} else if left_node is ast.None {
|
||||
g.gen_expr_to_string(left_node, ast.none_type)
|
||||
return true
|
||||
} else if node.left_type.has_flag(.option) {
|
||||
g.gen_expr_to_string(left_node, g.unwrap_generic(node.left_type))
|
||||
return true
|
||||
}
|
||||
g.get_str_fn(rec_type)
|
||||
return false
|
||||
@ -980,8 +987,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
||||
}
|
||||
|
||||
mut typ_sym := g.table.sym(unwrapped_rec_type)
|
||||
// alias type that undefined this method (not include `str`) need to use parent type
|
||||
if typ_sym.kind == .alias && node.name != 'str' && !typ_sym.has_method(node.name) {
|
||||
// non-option alias type that undefined this method (not include `str`) need to use parent type
|
||||
if !left_type.has_flag(.option) && typ_sym.kind == .alias && node.name != 'str'
|
||||
&& !typ_sym.has_method(node.name) {
|
||||
unwrapped_rec_type = (typ_sym.info as ast.Alias).parent_type
|
||||
typ_sym = g.table.sym(unwrapped_rec_type)
|
||||
} else if typ_sym.kind == .array && !typ_sym.has_method(node.name) {
|
||||
@ -2354,6 +2362,9 @@ fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang as
|
||||
g.expr(arg.expr)
|
||||
g.write('->val')
|
||||
return
|
||||
} else if expected_type.has_flag(.option) {
|
||||
g.expr_with_opt(arg.expr, arg_typ, expected_type)
|
||||
return
|
||||
} else if arg.expr is ast.ArrayInit {
|
||||
if arg.expr.is_fixed {
|
||||
if !arg.expr.has_it {
|
||||
|
@ -97,7 +97,10 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
|
||||
g.gen_plain_infix_expr(node)
|
||||
return
|
||||
}
|
||||
if (left.typ.is_ptr() && right.typ.is_int()) || (right.typ.is_ptr() && left.typ.is_int()) {
|
||||
is_none_check := node.left_type.has_flag(.option) && node.right is ast.None
|
||||
if is_none_check {
|
||||
g.gen_is_none_check(node)
|
||||
} else if (left.typ.is_ptr() && right.typ.is_int()) || (right.typ.is_ptr() && left.typ.is_int()) {
|
||||
g.gen_plain_infix_expr(node)
|
||||
} else if (left.typ.idx() == ast.string_type_idx || (!has_defined_eq_operator
|
||||
&& left.unaliased.idx() == ast.string_type_idx)) && node.right is ast.StringLiteral
|
||||
@ -951,6 +954,19 @@ fn (mut g Gen) infix_expr_and_or_op(node ast.InfixExpr) {
|
||||
g.gen_plain_infix_expr(node)
|
||||
}
|
||||
|
||||
fn (mut g Gen) gen_is_none_check(node ast.InfixExpr) {
|
||||
stmt_str := g.go_before_stmt(0).trim_space()
|
||||
g.empty_line = true
|
||||
left_var := g.expr_with_opt(node.left, node.left_type, node.left_type)
|
||||
g.writeln(';')
|
||||
g.write(stmt_str)
|
||||
g.write(' ')
|
||||
|
||||
g.write('${left_var}.state')
|
||||
g.write(' ${node.op.str()} ')
|
||||
g.write('2') // none state
|
||||
}
|
||||
|
||||
// gen_plain_infix_expr generates basic code for infix expressions,
|
||||
// without any overloading of any kind
|
||||
// i.e. v`a + 1` => c`a + 1`
|
||||
|
@ -63,8 +63,8 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|
||||
typ = typ.clear_flag(.shared_f).set_nr_muls(0)
|
||||
}
|
||||
mut sym := g.table.sym(typ)
|
||||
// when type is alias and doesn't has `str()`, print the aliased value
|
||||
if mut sym.info is ast.Alias && !sym.has_method('str') {
|
||||
// when type is non-option alias and doesn't has `str()`, print the aliased value
|
||||
if mut sym.info is ast.Alias && !sym.has_method('str') && !etype.has_flag(.option) {
|
||||
parent_sym := g.table.sym(sym.info.parent_type)
|
||||
if parent_sym.has_method('str') {
|
||||
typ = sym.info.parent_type
|
||||
@ -103,9 +103,11 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|
||||
}
|
||||
} else if sym_has_str_method
|
||||
|| sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] {
|
||||
is_ptr := typ.is_ptr()
|
||||
unwrap_option := expr is ast.Ident && (expr as ast.Ident).or_expr.kind == .propagate_option
|
||||
exp_typ := if unwrap_option { typ.clear_flag(.option) } else { typ }
|
||||
is_ptr := exp_typ.is_ptr()
|
||||
is_var_mut := expr.is_auto_deref_var()
|
||||
str_fn_name := g.get_str_fn(typ)
|
||||
str_fn_name := g.get_str_fn(exp_typ)
|
||||
if is_ptr && !is_var_mut {
|
||||
ref_str := '&'.repeat(typ.nr_muls())
|
||||
g.write('str_intp(1, _MOV((StrIntpData[]){{_SLIT("${ref_str}"), ${si_s_code} ,{.d_s = isnil(')
|
||||
@ -126,7 +128,12 @@ fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
|
||||
}
|
||||
}
|
||||
}
|
||||
g.expr_with_cast(expr, typ, typ)
|
||||
if unwrap_option {
|
||||
g.expr(expr)
|
||||
} else {
|
||||
g.expr_with_cast(expr, typ, typ)
|
||||
}
|
||||
|
||||
if is_shared {
|
||||
g.write('->val')
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
|
||||
} else {
|
||||
g.write('&(${basetyp}){')
|
||||
}
|
||||
} else if node.typ.has_flag(.option) {
|
||||
g.write('(${g.base_type(node.typ)}){')
|
||||
} else if g.inside_cinit {
|
||||
if is_multiline {
|
||||
g.writeln('{')
|
||||
|
@ -8,6 +8,10 @@ import v.token
|
||||
|
||||
fn (mut p Parser) array_init() ast.ArrayInit {
|
||||
first_pos := p.tok.pos()
|
||||
is_option := p.tok.kind == .question
|
||||
if is_option {
|
||||
p.next()
|
||||
}
|
||||
mut last_pos := p.tok.pos()
|
||||
p.check(.lsbr)
|
||||
// p.warn('array_init() exp=$p.expected_type')
|
||||
@ -40,6 +44,9 @@ fn (mut p Parser) array_init() ast.ArrayInit {
|
||||
} else {
|
||||
array_type = ast.new_type(idx)
|
||||
}
|
||||
if is_option {
|
||||
array_type = array_type.set_flag(.option)
|
||||
}
|
||||
has_type = true
|
||||
}
|
||||
last_pos = p.tok.pos()
|
||||
@ -107,7 +114,8 @@ fn (mut p Parser) array_init() ast.ArrayInit {
|
||||
last_pos = p.tok.pos()
|
||||
p.check(.rcbr)
|
||||
} else {
|
||||
p.warn_with_pos('use e.g. `x := [1]Type{}` instead of `x := [1]Type`',
|
||||
modifier := if is_option { '?' } else { '' }
|
||||
p.warn_with_pos('use e.g. `x := ${modifier}[1]Type{}` instead of `x := ${modifier}[1]Type`',
|
||||
first_pos.extend(last_pos))
|
||||
}
|
||||
} else {
|
||||
@ -126,7 +134,9 @@ fn (mut p Parser) array_init() ast.ArrayInit {
|
||||
}
|
||||
if exprs.len == 0 && p.tok.kind != .lcbr && has_type {
|
||||
if !p.pref.is_fmt {
|
||||
p.warn_with_pos('use `x := []Type{}` instead of `x := []Type`', first_pos.extend(last_pos))
|
||||
modifier := if is_option { '?' } else { '' }
|
||||
p.warn_with_pos('use `x := ${modifier}[]Type{}` instead of `x := ${modifier}[]Type`',
|
||||
first_pos.extend(last_pos))
|
||||
}
|
||||
}
|
||||
mut has_len := false
|
||||
@ -141,6 +151,9 @@ fn (mut p Parser) array_init() ast.ArrayInit {
|
||||
attr_pos = p.tok.pos()
|
||||
key := p.check_name()
|
||||
p.check(.colon)
|
||||
if is_option {
|
||||
p.error('Option array cannot have initializers')
|
||||
}
|
||||
match key {
|
||||
'len' {
|
||||
has_len = true
|
||||
|
@ -104,6 +104,10 @@ pub fn (mut p Parser) parse_array_type(expecting token.Kind) ast.Type {
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) parse_map_type() ast.Type {
|
||||
is_option := p.tok.kind == .question && p.peek_tok.kind == .name // option map
|
||||
if is_option {
|
||||
p.next()
|
||||
}
|
||||
p.next()
|
||||
if p.tok.kind != .lsbr {
|
||||
return ast.map_type
|
||||
@ -143,7 +147,11 @@ pub fn (mut p Parser) parse_map_type() ast.Type {
|
||||
if key_type.has_flag(.generic) || value_type.has_flag(.generic) {
|
||||
return ast.new_type(idx).set_flag(.generic)
|
||||
}
|
||||
return ast.new_type(idx)
|
||||
if is_option {
|
||||
return ast.new_type(idx).set_flag(.option)
|
||||
} else {
|
||||
return ast.new_type(idx)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut p Parser) parse_chan_type() ast.Type {
|
||||
|
@ -2100,6 +2100,10 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt {
|
||||
|
||||
pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
|
||||
// p.warn('name ')
|
||||
is_option := p.tok.kind == .question && p.peek_tok.kind == .lsbr
|
||||
if is_option {
|
||||
p.next()
|
||||
}
|
||||
is_shared := p.tok.kind == .key_shared
|
||||
is_atomic := p.tok.kind == .key_atomic
|
||||
if is_shared {
|
||||
@ -2129,6 +2133,7 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
|
||||
scope: p.scope
|
||||
}
|
||||
}
|
||||
in_select := p.prev_tok.kind == .arrow
|
||||
pos := p.tok.pos()
|
||||
mut name := p.check_name()
|
||||
if name == '_' {
|
||||
@ -2142,6 +2147,7 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
|
||||
is_mut: false
|
||||
is_static: false
|
||||
is_volatile: false
|
||||
is_option: is_option
|
||||
}
|
||||
scope: p.scope
|
||||
}
|
||||
@ -2152,6 +2158,20 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
|
||||
if p.expr_mod.len > 0 {
|
||||
name = '${p.expr_mod}.${name}'
|
||||
}
|
||||
|
||||
// parsers ident like var?, except on '<- var' '$if ident ?', '[if define ?]'
|
||||
allowed_cases := !in_select && !p.inside_comptime_if && !p.inside_ct_if_expr
|
||||
mut or_kind := ast.OrKind.absent
|
||||
mut or_stmts := []ast.Stmt{}
|
||||
mut or_pos := token.Pos{}
|
||||
|
||||
if allowed_cases && p.tok.kind == .question && p.peek_tok.kind != .lpar { // var?, not var?(
|
||||
or_kind = ast.OrKind.propagate_option
|
||||
p.check(.question)
|
||||
} else if allowed_cases && p.tok.kind == .key_orelse {
|
||||
or_kind = ast.OrKind.block
|
||||
or_stmts, or_pos = p.or_block(.no_err_var)
|
||||
}
|
||||
return ast.Ident{
|
||||
tok_kind: p.tok.kind
|
||||
kind: .unresolved
|
||||
@ -2166,9 +2186,15 @@ pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident {
|
||||
is_mut: is_mut
|
||||
is_static: is_static
|
||||
is_volatile: is_volatile
|
||||
is_option: or_kind != ast.OrKind.absent
|
||||
share: ast.sharetype_from_flags(is_shared, is_atomic)
|
||||
}
|
||||
scope: p.scope
|
||||
or_expr: ast.OrExpr{
|
||||
kind: or_kind
|
||||
stmts: or_stmts
|
||||
pos: or_pos
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2338,6 +2364,7 @@ fn (mut p Parser) is_generic_cast() bool {
|
||||
pub fn (mut p Parser) name_expr() ast.Expr {
|
||||
prev_tok_kind := p.prev_tok.kind
|
||||
mut node := ast.empty_expr
|
||||
|
||||
if p.expecting_type {
|
||||
if p.tok.kind == .dollar {
|
||||
node = p.parse_comptime_type()
|
||||
@ -2365,7 +2392,8 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||
// p.warn('resetting')
|
||||
p.expr_mod = ''
|
||||
// `map[string]int` initialization
|
||||
if p.tok.lit == 'map' && p.peek_tok.kind == .lsbr {
|
||||
if (p.tok.lit == 'map' && p.peek_tok.kind == .lsbr)
|
||||
|| (p.tok.kind == .question && p.peek_tok.lit == 'map') {
|
||||
mut pos := p.tok.pos()
|
||||
map_type := p.parse_map_type()
|
||||
if p.tok.kind == .lcbr {
|
||||
@ -2484,12 +2512,21 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||
p.check(.dot)
|
||||
p.expr_mod = mod
|
||||
}
|
||||
is_option := p.tok.kind == .question
|
||||
lit0_is_capital := if p.tok.kind != .eof && p.tok.lit.len > 0 {
|
||||
p.tok.lit[0].is_capital()
|
||||
if is_option {
|
||||
if p.peek_tok.kind != .eof && p.peek_tok.lit.len > 0 {
|
||||
p.peek_tok.lit[0].is_capital()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
p.tok.lit[0].is_capital()
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
is_option := p.tok.kind == .question
|
||||
|
||||
is_generic_call := p.is_generic_call()
|
||||
is_generic_cast := p.is_generic_cast()
|
||||
is_generic_struct_init := p.is_generic_struct_init()
|
||||
@ -2506,9 +2543,16 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||
}
|
||||
}
|
||||
} else if p.peek_tok.kind == .lpar || is_generic_call || is_generic_cast
|
||||
|| (is_option && p.peek_token(2).kind == .lpar) {
|
||||
|| (is_option && p.peek_token(2).kind == .lpar) || (is_option && p.peek_tok.kind == .lsbr
|
||||
&& p.peek_token(2).kind == .rsbr && p.peek_token(3).kind == .name
|
||||
&& p.peek_token(4).kind == .lpar) {
|
||||
is_array := p.peek_tok.kind == .lsbr
|
||||
// foo(), foo<int>() or type() cast
|
||||
mut name := if is_option { p.peek_tok.lit } else { p.tok.lit }
|
||||
mut name := if is_option {
|
||||
if is_array { p.peek_token(3).lit } else { p.peek_tok.lit }
|
||||
} else {
|
||||
p.tok.lit
|
||||
}
|
||||
if mod.len > 0 {
|
||||
name = '${mod}.${name}'
|
||||
}
|
||||
@ -2574,7 +2618,8 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (p.peek_tok.kind == .lcbr || is_generic_struct_init)
|
||||
} else if (((!is_option && p.peek_tok.kind == .lcbr)
|
||||
|| (is_option && p.peek_token(2).kind == .lcbr)) || is_generic_struct_init)
|
||||
&& (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital))
|
||||
&& !p.inside_match_case && (!p.inside_if || p.inside_select)
|
||||
&& (!p.inside_for || p.inside_select) && !known_var {
|
||||
@ -2662,8 +2707,9 @@ pub fn (mut p Parser) name_expr() ast.Expr {
|
||||
}
|
||||
p.expr_mod = ''
|
||||
return node
|
||||
} else if p.tok.kind == .question && p.peek_tok.kind == .lsbr {
|
||||
return p.array_init()
|
||||
}
|
||||
|
||||
ident := p.parse_ident(language)
|
||||
node = ident
|
||||
if p.inside_defer {
|
||||
|
@ -27,7 +27,7 @@ fn (e MyError) code() int {
|
||||
}
|
||||
|
||||
// An example function that returns a custom error.
|
||||
fn foo() ?string {
|
||||
fn foo() !string {
|
||||
return MyError{
|
||||
msg: 'foo'
|
||||
blah: 'world'
|
||||
|
16
vlib/v/tests/option_call_propagation_test.v
Normal file
16
vlib/v/tests/option_call_propagation_test.v
Normal file
@ -0,0 +1,16 @@
|
||||
fn a() ?int {
|
||||
return none
|
||||
}
|
||||
|
||||
fn abc() ?int {
|
||||
varz := a()
|
||||
dump(varz)
|
||||
assert varz == none
|
||||
return varz
|
||||
}
|
||||
|
||||
fn test_option_call_propagation() {
|
||||
var := abc() or { 1 }
|
||||
assert var == 1
|
||||
println(var)
|
||||
}
|
@ -30,7 +30,7 @@ fn encode_struct[T](val T) map[string][]string {
|
||||
// work if comment lines 27 and 28
|
||||
write1(value)
|
||||
wr.write2(value)
|
||||
out[field.name] << '${value}'
|
||||
out[field.name] << '${value:d}'
|
||||
} $else {
|
||||
write1(value)
|
||||
wr.write2(value)
|
||||
@ -40,7 +40,7 @@ fn encode_struct[T](val T) map[string][]string {
|
||||
$if field.is_option {
|
||||
write1(value)
|
||||
wr.write2(value)
|
||||
out[field.name] << '${value}'
|
||||
out[field.name] << '${value:d}'
|
||||
} $else {
|
||||
write1(value)
|
||||
wr.write2(value)
|
||||
|
@ -401,7 +401,7 @@ fn test_option_c_struct_gen() {
|
||||
_ := get_opt_to_c_struct() or { C.stat{} }
|
||||
}
|
||||
|
||||
// For issue #16062: checker disallow the return of voidptr(nil) in or block
|
||||
// For issue #16062: checker disallowed the return of voidptr(nil) in or block
|
||||
struct Bar {}
|
||||
|
||||
fn get_bar(should_return_value bool) ?&Bar {
|
||||
@ -411,7 +411,7 @@ fn get_bar(should_return_value bool) ?&Bar {
|
||||
return none
|
||||
}
|
||||
|
||||
fn test_() {
|
||||
fn test_allow_returning_an_optional_pointer_to_a_struct() {
|
||||
a := get_bar(true)?
|
||||
assert a == unsafe { nil }
|
||||
//
|
||||
@ -424,3 +424,19 @@ fn test_() {
|
||||
get_bar(false) or { unsafe { nil } }
|
||||
assert true
|
||||
}
|
||||
|
||||
struct AFoo {
|
||||
mut:
|
||||
name string
|
||||
}
|
||||
|
||||
fn (mut f AFoo) opt_string(arr ?[]int) ?string {
|
||||
return arr?.len.str()
|
||||
}
|
||||
|
||||
fn test_creating_an_option_from_a_struct_value() {
|
||||
mut m := ?AFoo(AFoo{})
|
||||
assert m?.opt_string([1, 2, 3])? == '3'
|
||||
m?.name = 'foo'
|
||||
assert m?.name == 'foo'
|
||||
}
|
||||
|
61
vlib/v/tests/option_var_cast_test.v
Normal file
61
vlib/v/tests/option_var_cast_test.v
Normal file
@ -0,0 +1,61 @@
|
||||
type SumType = f64 | int
|
||||
|
||||
type MyByte = u8
|
||||
|
||||
struct Struct {
|
||||
a int
|
||||
}
|
||||
|
||||
type Alias = int
|
||||
|
||||
fn test_main() {
|
||||
arr := [1, 2, 3]
|
||||
mut t := ?[]int([]int{len: 10, init: 2})
|
||||
assert t != none
|
||||
t = ?[]int(arr)
|
||||
assert t != none
|
||||
t = ?[]int([]int{len: 1, init: 0})
|
||||
assert t != none
|
||||
|
||||
mut t2 := ?int(1)
|
||||
assert t2 != none
|
||||
mut t3 := ?f64(1.2)
|
||||
assert t3 != none
|
||||
mut t4 := ?string('')
|
||||
assert t4 != none
|
||||
mut t5 := ?SumType(1)
|
||||
assert t5 != none
|
||||
mut t6 := ?SumType(none)
|
||||
assert t6 == none
|
||||
mut t7 := ?Struct(Struct{})
|
||||
assert t7 != none
|
||||
}
|
||||
|
||||
fn test_cast() {
|
||||
var := ?u8(1)
|
||||
println(?u8(var))
|
||||
println(?u8(?u8(255)))
|
||||
println(?int(none))
|
||||
println(?int(var?))
|
||||
|
||||
a := ?Struct{}
|
||||
assert a == none
|
||||
b := ?Struct(Struct{})
|
||||
assert b != none
|
||||
|
||||
mut v1 := ?bool(1)
|
||||
assert v1?.str() == 'true'
|
||||
v1 = ?bool(true)
|
||||
assert v1?.str() == 'true'
|
||||
v1 = ?bool(0)
|
||||
assert v1?.str() == 'false'
|
||||
v1 = ?bool(false)
|
||||
assert v1?.str() == 'false'
|
||||
}
|
||||
|
||||
fn test_cast_alias() {
|
||||
assert ?MyByte(0).str() == 'Option(MyByte(0))'
|
||||
assert ?MyByte(255).str() == 'Option(MyByte(255))'
|
||||
assert ?MyByte(?u8(0)).str() == 'Option(MyByte(0))'
|
||||
assert ?MyByte(?u8(255)).str() == 'Option(MyByte(255))'
|
||||
}
|
23
vlib/v/tests/option_var_map_test.v
Normal file
23
vlib/v/tests/option_var_map_test.v
Normal file
@ -0,0 +1,23 @@
|
||||
fn abc(a ?map[string]string) ?string {
|
||||
return a?['foo']
|
||||
}
|
||||
|
||||
fn abc_2(a ?map[string]string) ?string {
|
||||
return a?['foo']
|
||||
}
|
||||
|
||||
fn test_option_map() {
|
||||
mut var4 := ?map[string]string{}
|
||||
assert var4 == none
|
||||
var4 = {
|
||||
'': ''
|
||||
}
|
||||
var4?['foo'] = 'foo'
|
||||
assert abc(var4)? == 'foo'
|
||||
assert abc_2(var4)? == 'foo'
|
||||
assert var4 != none
|
||||
assert var4? == {
|
||||
'': ''
|
||||
'foo': 'foo'
|
||||
}
|
||||
}
|
210
vlib/v/tests/option_var_test.v
Normal file
210
vlib/v/tests/option_var_test.v
Normal file
@ -0,0 +1,210 @@
|
||||
import time
|
||||
|
||||
type MyAlias = []int
|
||||
type MySumType = []f64 | []int
|
||||
|
||||
struct Test {
|
||||
}
|
||||
|
||||
struct StructType2 {
|
||||
a ?time.Time
|
||||
}
|
||||
|
||||
struct StructType {
|
||||
mut:
|
||||
a string
|
||||
b ?int
|
||||
c ?f64
|
||||
d ?[]string
|
||||
e ?MyAlias
|
||||
f ?MySumType
|
||||
}
|
||||
|
||||
struct Decoder {}
|
||||
|
||||
fn (d &Decoder) decode[T](typ T) T {
|
||||
$for field in T.fields {
|
||||
$if field.is_option {
|
||||
dump(typ.$(field.name) ?.str())
|
||||
typ.$(field.name) = none
|
||||
dump(typ.$(field.name) ?.str())
|
||||
}
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
fn test_comptime() {
|
||||
d := Decoder{}
|
||||
result := d.decode(StructType{
|
||||
a: 'foo'
|
||||
b: 3
|
||||
})
|
||||
println(result)
|
||||
}
|
||||
|
||||
fn test_cast_option() {
|
||||
mut x := ?int(123)
|
||||
dump(x)
|
||||
assert x != none
|
||||
x = none
|
||||
assert x == none
|
||||
dump(x)
|
||||
}
|
||||
|
||||
fn test_assign_from_option() {
|
||||
mut x := ?int(123)
|
||||
mut y := x
|
||||
println(y)
|
||||
assert x != none
|
||||
assert y != none
|
||||
assert x? == 123
|
||||
assert y? == 123
|
||||
}
|
||||
|
||||
fn test_blank_assign() {
|
||||
_ := ?bool(false)
|
||||
}
|
||||
|
||||
fn test_optional_value_assign() {
|
||||
x := ?int(0)
|
||||
assert x != none
|
||||
assert x? == 0
|
||||
}
|
||||
|
||||
fn test_assert_initialized() {
|
||||
mut x := ?int(1)
|
||||
mut y := ?int(1)
|
||||
assert x != none
|
||||
assert y != none
|
||||
}
|
||||
|
||||
fn test_comptime_checks() {
|
||||
val := StructType{
|
||||
a: 'foo'
|
||||
b: 3
|
||||
}
|
||||
$for field in StructType.fields {
|
||||
value := val.$(field.name)
|
||||
$if field.is_option {
|
||||
var := val.$(field.name)
|
||||
var2 := var
|
||||
dump(var)
|
||||
dump(var2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_none_initialization() {
|
||||
mut var := ?int(none)
|
||||
mut var2 := ?int(none)
|
||||
mut var3 := ?int(none)
|
||||
|
||||
assert var == none
|
||||
assert var2 == none
|
||||
assert var3 == none
|
||||
|
||||
var = 1
|
||||
var2 = 2
|
||||
var3 = 3
|
||||
|
||||
assert var? == 1
|
||||
assert var2? == 2
|
||||
assert var3? == 3
|
||||
|
||||
assert var != none
|
||||
assert var2 != none
|
||||
assert var3 != none
|
||||
}
|
||||
|
||||
fn test_as_cast() {
|
||||
var := StructType2{}
|
||||
b := var.a as ?time.Time
|
||||
}
|
||||
|
||||
fn test_unwrap() {
|
||||
var := ?int(1)
|
||||
println(var)
|
||||
assert var != none
|
||||
assert var? == 1
|
||||
|
||||
var2 := var? + 1
|
||||
println(var2)
|
||||
|
||||
assert var2 == 2
|
||||
}
|
||||
|
||||
fn test_or_block() {
|
||||
var1 := ?int(none)
|
||||
var2 := var1 or { 0 }
|
||||
var3 := var1 or { 1 }
|
||||
assert var2 + var3 == 1
|
||||
var4 := var1 or {
|
||||
t := 1 + var3
|
||||
t
|
||||
}
|
||||
|
||||
assert var4 == 2
|
||||
}
|
||||
|
||||
fn test_default_values() {
|
||||
var_none := ?f64(none)
|
||||
var_none2 := ?string(none)
|
||||
|
||||
var2 := var_none or { 1.0 }
|
||||
println(var2)
|
||||
assert var2 == 1.0
|
||||
|
||||
var3 := var_none2 or { 'foo' }
|
||||
println(var3)
|
||||
assert var3 == 'foo'
|
||||
}
|
||||
|
||||
fn test_assert_option() {
|
||||
var1 := ?int(none)
|
||||
varz := ?f64(none)
|
||||
assert var1 == none
|
||||
|
||||
var2 := var1 or { 1 }
|
||||
|
||||
assert var1 == none
|
||||
assert var2 == 1
|
||||
|
||||
println(var2)
|
||||
|
||||
var3 := varz or { 0.0 }
|
||||
assert var3 == 0
|
||||
|
||||
var4 := varz
|
||||
assert var4 == none
|
||||
}
|
||||
|
||||
fn test_opt_assign() {
|
||||
mut var1 := ?int(none)
|
||||
assert var1 == none
|
||||
var1 = 1
|
||||
assert var1 != none
|
||||
var2 := var1
|
||||
assert var2? == var1?
|
||||
}
|
||||
|
||||
fn test_opt_none() {
|
||||
mut t1 := ?int(none)
|
||||
t2 := ?int(none)
|
||||
|
||||
t1 = t2
|
||||
assert t1 == none
|
||||
assert t2 == none
|
||||
|
||||
mut t3 := ?Test{}
|
||||
t4 := t3
|
||||
assert t4 == none
|
||||
assert t3 == none
|
||||
|
||||
mut t5 := ?map[string]string{}
|
||||
t7 := {
|
||||
'foo': 'bar'
|
||||
}
|
||||
t6 := if t5 != none { t5?.clone() } else { t7 }
|
||||
assert t5 == none
|
||||
assert t6.len == 1
|
||||
}
|
36
vlib/v/tests/var_option_arr_test.v
Normal file
36
vlib/v/tests/var_option_arr_test.v
Normal file
@ -0,0 +1,36 @@
|
||||
fn abc() ?[]int {
|
||||
return [1, 2, 3]
|
||||
}
|
||||
|
||||
fn arr_opt(arr ?[]string) ? {
|
||||
assert arr != none
|
||||
assert arr?.len != 0
|
||||
|
||||
for k, v in arr {
|
||||
assert arr?[k] == 'foo'
|
||||
assert v == 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
mut var2 := abc()?
|
||||
assert var2.len == 3
|
||||
|
||||
mut var := ?[]int{}
|
||||
assert var == none
|
||||
if var == none {
|
||||
var = [1]
|
||||
}
|
||||
assert var != none
|
||||
assert var?[0] == 1
|
||||
assert var?.len == 1
|
||||
|
||||
mut var3 := ?[]string{}
|
||||
assert var3 == none
|
||||
var3 = ['foo']
|
||||
assert var3 != none
|
||||
|
||||
arr_opt(var3)
|
||||
arr_opt([]string{len: 4, init: 'foo'})
|
||||
arr_opt(['foo', 'foo', 'foo', 'foo'])
|
||||
}
|
20
vlib/v/tests/var_option_as_arg_test.v
Normal file
20
vlib/v/tests/var_option_as_arg_test.v
Normal file
@ -0,0 +1,20 @@
|
||||
fn is_none(a ?string) bool {
|
||||
return a == none
|
||||
}
|
||||
|
||||
fn is_map_none(a ?map[string]int) bool {
|
||||
return a == none
|
||||
}
|
||||
|
||||
fn test_opt_call_arg() {
|
||||
assert is_none(?string(none))
|
||||
var := ?string('foo')
|
||||
assert is_none(var) == false
|
||||
|
||||
mut var2 := ?map[string]int{}
|
||||
assert var2 == none
|
||||
var2 = {
|
||||
'a': 1
|
||||
}
|
||||
assert is_map_none(var2) == false
|
||||
}
|
25
vlib/v/tests/var_option_comptime_test.v
Normal file
25
vlib/v/tests/var_option_comptime_test.v
Normal file
@ -0,0 +1,25 @@
|
||||
struct Test {
|
||||
a ?string
|
||||
}
|
||||
|
||||
fn foo(a ?string) ? {
|
||||
println(a?)
|
||||
if a == none {
|
||||
assert false
|
||||
}
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
v := Test{}
|
||||
$for f in Test.fields {
|
||||
mut t := v.$(f.name)
|
||||
assert t == none
|
||||
z := t or { '' }
|
||||
assert z == ''
|
||||
foo(t)
|
||||
t = 'foo'
|
||||
assert t != none
|
||||
foo(t)
|
||||
assert t? == 'foo'
|
||||
}
|
||||
}
|
27
vlib/v/tests/var_option_opt_arg_test.v
Normal file
27
vlib/v/tests/var_option_opt_arg_test.v
Normal file
@ -0,0 +1,27 @@
|
||||
fn option_arg(x ?int) ?int {
|
||||
assert x != none
|
||||
return x
|
||||
}
|
||||
|
||||
fn option_arg2(x ?f64, y ?int, z ?string) ?string {
|
||||
assert x != none
|
||||
assert y != none
|
||||
assert z != none
|
||||
return z
|
||||
}
|
||||
|
||||
fn option_arg3(x ?f64, y ?int, z ?string) bool {
|
||||
assert x == none
|
||||
assert y == none
|
||||
assert z == none
|
||||
return true
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
var := ?int(1)
|
||||
assert option_arg(var)? == 1
|
||||
assert option_arg(100)? == 100
|
||||
assert option_arg2(1.1, 1, '')? == ''
|
||||
assert option_arg2(1.2, 2, 'foo')? == 'foo'
|
||||
assert option_arg3(none, none, none)
|
||||
}
|
17
vlib/v/tests/var_option_opt_multi_return_test.v
Normal file
17
vlib/v/tests/var_option_opt_multi_return_test.v
Normal file
@ -0,0 +1,17 @@
|
||||
fn ret_opt() ?string {
|
||||
return 'foo'
|
||||
}
|
||||
|
||||
fn option_arg(x ?int) (?string, ?int) {
|
||||
assert x == none
|
||||
return '1', x
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
var := ret_opt()?
|
||||
assert var == 'foo'
|
||||
|
||||
var1, var2 := option_arg(none)
|
||||
assert var1 != none
|
||||
assert var2 == none
|
||||
}
|
49
vlib/v/tests/var_option_struct_test.v
Normal file
49
vlib/v/tests/var_option_struct_test.v
Normal file
@ -0,0 +1,49 @@
|
||||
struct Foo {
|
||||
mut:
|
||||
name string
|
||||
}
|
||||
|
||||
struct Test {
|
||||
pub mut:
|
||||
a string
|
||||
b ?string
|
||||
}
|
||||
|
||||
fn (mut f Foo) test(arr ?[]int) ?string {
|
||||
return arr?.len.str()
|
||||
}
|
||||
|
||||
fn (mut f Foo) test2() ?string {
|
||||
return none
|
||||
}
|
||||
|
||||
fn fn_a(s ?Test) ? {
|
||||
println(s?.a)
|
||||
assert false
|
||||
}
|
||||
|
||||
fn fn_b(s ?Test) ? {
|
||||
println(s?.b?)
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_main() {
|
||||
mut m := ?Foo{}
|
||||
assert m == none
|
||||
m = Foo{
|
||||
name: 'foo'
|
||||
}
|
||||
assert m != none
|
||||
v := m?.test([1, 2, 3]) or { '4' }
|
||||
m?.name = 'foo'
|
||||
assert m?.name == 'foo'
|
||||
assert v == '3'
|
||||
var := m?.test2() or { '' }
|
||||
assert var == ''
|
||||
}
|
||||
|
||||
fn test_opt_call() {
|
||||
mut t := ?Test{}
|
||||
fn_a(none) // returns none
|
||||
fn_b(t) // returs none
|
||||
}
|
12
vlib/v/tests/var_option_sumtype_test.v
Normal file
12
vlib/v/tests/var_option_sumtype_test.v
Normal file
@ -0,0 +1,12 @@
|
||||
type Abc = f64 | int
|
||||
|
||||
fn test_main() {
|
||||
mut v := ?Abc(none)
|
||||
assert v == none
|
||||
println(v)
|
||||
v = Abc(1)
|
||||
assert v != none
|
||||
v = 1.2
|
||||
println(v)
|
||||
assert v != none
|
||||
}
|
@ -31,7 +31,13 @@ pub fn decode[T](src string) !T {
|
||||
}
|
||||
}
|
||||
|
||||
$if field.typ is u8 {
|
||||
$if field.is_enum {
|
||||
typ.$(field.name) = if key := res[field.name] {
|
||||
key.int()
|
||||
} else {
|
||||
res[json_name]!.int()
|
||||
}
|
||||
} $else $if field.typ is u8 {
|
||||
typ.$(field.name) = res[json_name]!.u64()
|
||||
} $else $if field.typ is u16 {
|
||||
typ.$(field.name) = res[json_name]!.u64()
|
||||
@ -62,12 +68,6 @@ pub fn decode[T](src string) !T {
|
||||
} $else $if field.is_array {
|
||||
// typ.$(field.name) = res[field.name]!.arr()
|
||||
} $else $if field.is_struct {
|
||||
} $else $if field.is_enum {
|
||||
typ.$(field.name) = if key := res[field.name] {
|
||||
key.int()
|
||||
} else {
|
||||
res[json_name]!.int()
|
||||
}
|
||||
} $else $if field.is_alias {
|
||||
} $else $if field.is_map {
|
||||
} $else {
|
||||
|
Loading…
Reference in New Issue
Block a user