diff --git a/cmd/tools/vdoc/vdoc.v b/cmd/tools/vdoc/vdoc.v index 8d6e392f40..9cb90983b8 100644 --- a/cmd/tools/vdoc/vdoc.v +++ b/cmd/tools/vdoc/vdoc.v @@ -207,7 +207,7 @@ fn (vd VDoc) get_readme(path string) string { return readme_contents } -fn (vd VDoc) emit_generate_err(err Error) { +fn (vd VDoc) emit_generate_err(err IError) { cfg := vd.cfg mut err_msg := err.msg if err.code == 1 { diff --git a/vlib/builtin/option.v b/vlib/builtin/option.v index 8662d6d515..6610de0a35 100644 --- a/vlib/builtin/option.v +++ b/vlib/builtin/option.v @@ -16,16 +16,20 @@ pub: code int } -pub struct Option3 { +struct Option3 { state byte - err IError + err IError = none__ } -[inline] -fn (e IError) str() string { - return e.msg +const none__ = IError(&None__{}) + +struct None__ { + msg string + code int } +fn (_ None__) str() string { return 'none' } + fn opt_ok3(data voidptr, mut option Option3, size int) { unsafe { *option = Option3{} @@ -34,16 +38,6 @@ fn opt_ok3(data voidptr, mut option Option3, size int) { } } -pub fn (o Option3) str() string { - if o.state == 0 { - return 'Option{ ok }' - } - if o.state == 1 { - return 'Option{ none }' - } - return 'Option{ err: "$o.err" }' -} - [inline] pub fn error3(message string) IError { return &Error{ @@ -73,13 +67,6 @@ struct Option2 { // derived Option2_xxx types } -[inline] -fn (e Error) str() string { - // TODO: this should probably have a better str method, - // but this minimizes the amount of broken code after #8924 - return e.msg -} - // `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }` fn opt_ok(data voidptr, mut option Option2, size int) { unsafe { @@ -89,16 +76,6 @@ fn opt_ok(data voidptr, mut option Option2, size int) { } } -pub fn (o Option2) str() string { - if o.state == 0 { - return 'Option{ ok }' - } - if o.state == 1 { - return 'Option{ none }' - } - return 'Option{ error: "$o.err" }' -} - // error returns an optional containing the error given in `message`. // `if ouch { return error('an error occurred') }` pub fn error2(message string) Option2 { diff --git a/vlib/sync/channel_opt_propagate_test.v b/vlib/sync/channel_opt_propagate_test.v index f9aea58d60..a7e26fe859 100644 --- a/vlib/sync/channel_opt_propagate_test.v +++ b/vlib/sync/channel_opt_propagate_test.v @@ -14,7 +14,7 @@ fn do_rec_calc_send(chs []chan i64, mut sem sync.Semaphore) { mut msg := '' for { mut s := get_val_from_chan(chs[0]) or { - msg = err.str() + msg = err.msg break } s++ diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 5798040c55..833e723647 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -413,7 +413,7 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) { c.check_expected(field_expr_type, field.typ) or { if !(sym.kind == .interface_ && c.type_implements(field_expr_type, field.typ, field.pos)) { - c.error('incompatible initializer for field `$field.name`: $err', + c.error('incompatible initializer for field `$field.name`: $err.msg', field.default_expr.position()) } } @@ -592,7 +592,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { expr_type_sym := c.table.get_type_symbol(expr_type) if expr_type != table.void_type && expr_type_sym.kind != .placeholder { c.check_expected(expr_type, embed_type) or { - c.error('cannot assign to field `$info_field.name`: $err', + c.error('cannot assign to field `$info_field.name`: $err.msg', field.pos) } } @@ -608,7 +608,7 @@ pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type { c.type_implements(expr_type, info_field.typ, field.pos) } else if expr_type != table.void_type && expr_type_sym.kind != .placeholder { c.check_expected(expr_type, info_field.typ) or { - c.error('cannot assign to field `$info_field.name`: $err', + c.error('cannot assign to field `$info_field.name`: $err.msg', field.pos) } } @@ -762,21 +762,21 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { elem_type := right.array_info().elem_type // if left_default.kind != right_sym.kind { c.check_expected(left_type, elem_type) or { - c.error('left operand to `$infix_expr.op` does not match the array element type: $err', + c.error('left operand to `$infix_expr.op` does not match the array element type: $err.msg', left_right_pos) } } .map { map_info := right.map_info() c.check_expected(left_type, map_info.key_type) or { - c.error('left operand to `$infix_expr.op` does not match the map key type: $err', + c.error('left operand to `$infix_expr.op` does not match the map key type: $err.msg', left_right_pos) } infix_expr.left_type = map_info.key_type } .string { c.check_expected(left_type, right_type) or { - c.error('left operand to `$infix_expr.op` does not match: $err', + c.error('left operand to `$infix_expr.op` does not match: $err.msg', left_right_pos) } } @@ -923,16 +923,28 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { return c.check_shift(left_type, right_type, left_pos, right_pos) } .key_is, .not_is { - type_expr := infix_expr.right as ast.Type - typ_sym := c.table.get_type_symbol(type_expr.typ) + right_expr := infix_expr.right + mut typ := match right_expr { + ast.Type { + right_expr.typ + } + ast.None { + table.none_type_idx + } + else { + c.error('invalid type `$right_expr`', right_expr.position()) + table.Type(0) + } + } + typ_sym := c.table.get_type_symbol(typ) + op := infix_expr.op.str() if typ_sym.kind == .placeholder { - c.error('$infix_expr.op.str(): type `$typ_sym.name` does not exist', type_expr.pos) + c.error('$op: type `$typ_sym.name` does not exist', right_expr.position()) } if left.kind !in [.interface_, .sum_type] { - c.error('`$infix_expr.op.str()` can only be used with interfaces and sum types', - infix_expr.pos) + c.error('`$op` can only be used with interfaces and sum types', infix_expr.pos) } else if mut left.info is table.SumType { - if type_expr.typ !in left.info.variants { + if typ !in left.info.variants { c.error('`$left.name` has no variant `$right.name`', infix_expr.pos) } } @@ -1411,7 +1423,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { } if left_type_sym.kind == .aggregate { // the error message contains the problematic type - unknown_method_msg = err + unknown_method_msg = err.msg } } if has_method { @@ -1493,7 +1505,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { // continue // } if got_arg_typ != table.void_type { - c.error('$err in argument ${i + 1} to `${left_type_sym.name}.$method_name`', + c.error('$err.msg in argument ${i + 1} to `${left_type_sym.name}.$method_name`', arg.pos) } } @@ -2014,7 +2026,7 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { if f.generic_names.len > 0 { continue } - c.error('$err in argument ${i + 1} to `$fn_name`', call_arg.pos) + c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos) } } if f.generic_names.len != call_expr.generic_types.len { @@ -2043,9 +2055,24 @@ fn (mut c Checker) type_implements(typ table.Type, inter_typ table.Type, pos tok utyp := c.unwrap_generic(typ) typ_sym := c.table.get_type_symbol(utyp) mut inter_sym := c.table.get_type_symbol(inter_typ) + // do not check the same type more than once + if mut inter_sym.info is table.Interface { + for t in inter_sym.info.types { + if t.idx() == utyp.idx() { + return true + } + } + } styp := c.table.type_to_str(utyp) - same_base_type := utyp.idx() == inter_typ.idx() - if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ && !same_base_type { + if utyp.idx() == inter_typ.idx() { + // same type -> already casted to the interface + return true + } + if inter_typ.idx() == table.error_type_idx && utyp.idx() == table.none_type_idx { + // `none` "implements" the Error interface + return true + } + if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ { c.error('cannot implement interface `$inter_sym.name` with a different interface `$styp`', pos) } @@ -2314,7 +2341,7 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T } } if sym.kind in [.aggregate, .sum_type] { - unknown_field_msg = err + unknown_field_msg = err.msg } } if !c.inside_unsafe { @@ -2884,7 +2911,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { && left_sym.kind != .interface_ { // Dual sides check (compatibility check) c.check_expected(right_type_unwrapped, left_type_unwrapped) or { - c.error('cannot assign to `$left`: $err', right.position()) + c.error('cannot assign to `$left`: $err.msg', right.position()) } } if left_sym.kind == .interface_ { @@ -3064,7 +3091,7 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type { continue } c.check_expected(typ, elem_type) or { - c.error('invalid array element: $err', expr.position()) + c.error('invalid array element: $err.msg', expr.position()) } } if array_init.is_fixed { @@ -4421,7 +4448,7 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type { // probably any mismatch will be caught by not producing a value instead for st in branch.stmts[0..branch.stmts.len - 1] { // must not contain C statements - st.check_c_expr() or { c.error('`match` expression branch has $err', st.pos) } + st.check_c_expr() or { c.error('`match` expression branch has $err.msg', st.pos) } } } // If the last statement is an expression, return its type @@ -5040,7 +5067,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { } for st in branch.stmts { // must not contain C statements - st.check_c_expr() or { c.error('`if` expression branch has $err', st.pos) } + st.check_c_expr() or { c.error('`if` expression branch has $err.msg', st.pos) } } } // Also check for returns inside a comp.if's statements, even if its contents aren't parsed diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v index 410b54091f..56ceea703b 100644 --- a/vlib/v/gen/c/auto_str_methods.v +++ b/vlib/v/gen/c/auto_str_methods.v @@ -142,9 +142,7 @@ fn (mut g Gen) gen_str_for_option(typ table.Type, styp string, str_fn_name strin g.type_definitions.writeln('string indent_${str_fn_name}($styp it, int indent_count); // auto') g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp it, int indent_count) {') g.auto_str_funcs.writeln('\tstring res;') - g.auto_str_funcs.writeln('\tif (it.state == 1) {') - g.auto_str_funcs.writeln('\t\tres = _SLIT("none");') - g.auto_str_funcs.writeln('\t} else if (it.state == 0) {') + g.auto_str_funcs.writeln('\tif (it.state == 0) {') if sym.kind == .string { g.auto_str_funcs.writeln('\t\tres = _STR("\'%.*s\\000\'", 2, ${parent_str_fn_name}(*($sym.cname*)it.data));') } else if should_use_indent_func(sym.kind) && !sym_has_str_method { @@ -153,7 +151,7 @@ fn (mut g Gen) gen_str_for_option(typ table.Type, styp string, str_fn_name strin g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(*($sym.cname*)it.data);') } g.auto_str_funcs.writeln('\t} else {') - g.auto_str_funcs.writeln('\t\tres = _STR("error: \'%.*s\\000\'", 2, it.err.msg);') + g.auto_str_funcs.writeln('\t\tres = _STR("error: %.*s\\000", 2, indent_IError_str(it.err, indent_count));') g.auto_str_funcs.writeln('\t}') g.auto_str_funcs.writeln('\treturn _STR("Option(%.*s\\000)", 2, res);') g.auto_str_funcs.writeln('}') diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 50e0671e81..ccb611bf8b 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -569,7 +569,7 @@ fn (mut g Gen) expr_string(expr ast.Expr) string { // if one location changes fn (mut g Gen) optional_type_name(t table.Type) (string, string) { base := g.base_type(t) - mut styp := 'Option2_$base' + mut styp := 'Option3_$base' if t.is_ptr() { styp = styp.replace('*', '_ptr') } @@ -581,7 +581,7 @@ fn (g &Gen) optional_type_text(styp string, base string) string { size := if base == 'void' { 'byte' } else { base } ret := 'struct $styp { byte state; - Error err; + IError err; byte data[sizeof($size)]; }' return ret @@ -652,13 +652,10 @@ fn (mut g Gen) register_chan_pop_optional_call(opt_el_type string, styp string) if opt_el_type !in g.chan_pop_optionals { g.chan_pop_optionals << opt_el_type g.channel_definitions.writeln(' -static inline $opt_el_type __Option2_${styp}_popval($styp ch) { +static inline $opt_el_type __Option3_${styp}_popval($styp ch) { $opt_el_type _tmp = {0}; if (sync__Channel_try_pop_priv(ch, _tmp.data, false)) { - Option2 _tmp2 = error2(_SLIT("channel closed")); - $opt_el_type _tmp3; - memcpy(&_tmp3, &_tmp2, sizeof(Option2)); - return _tmp3; + return ($opt_el_type){ .state = 2, .err = error3(_SLIT("channel closed")) }; } return _tmp; }') @@ -669,14 +666,11 @@ fn (mut g Gen) register_chan_push_optional_call(el_type string, styp string) { if styp !in g.chan_push_optionals { g.chan_push_optionals << styp g.channel_definitions.writeln(' -static inline Option2_void __Option2_${styp}_pushval($styp ch, $el_type e) { +static inline Option3_void __Option3_${styp}_pushval($styp ch, $el_type e) { if (sync__Channel_try_push_priv(ch, &e, false)) { - Option2 _tmp2 = error2(_SLIT("channel closed")); - Option2_void _tmp3; - memcpy(&_tmp3, &_tmp2, sizeof(Option2)); - return _tmp3; + return (Option3_void){ .state = 2, .err = error3(_SLIT("channel closed")) }; } - return (Option2_void){0}; + return (Option3_void){0}; }') } } @@ -913,10 +907,10 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) { sym := g.table.get_type_symbol(stmt.typ) if sym.name in ['Option2', 'Option3'] || stmt.expr is ast.None { tmp := g.new_tmp_var() - g.write('Option2 $tmp = ') + g.write('Option3 $tmp = (Option3){.state = 0,.err = ') g.expr(stmt.expr) - g.writeln(';') - g.writeln('memcpy(&$tmp_var, &$tmp, sizeof(Option2));') + g.writeln('};') + g.writeln('memcpy(&$tmp_var, &$tmp, sizeof(Option3));') } else { mut styp := g.base_type(stmt.typ) $if tinyc && x32 && windows { @@ -926,9 +920,9 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) { styp = 'f64' } } - g.write('opt_ok(&($styp[]) { ') + g.write('opt_ok3(&($styp[]) { ') g.stmt(stmt) - g.writeln(' }, (Option2*)(&$tmp_var), sizeof($styp));') + g.writeln(' }, (Option3*)(&$tmp_var), sizeof($styp));') } } } else { @@ -1602,8 +1596,9 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw table.Type, expected_t // allow using the new Error struct as a string, to avoid a breaking change // TODO: temporary to allow people to migrate their code; remove soon if got_type == table.error_type_idx && expected_type == table.string_type_idx { + g.write('(*(') g.expr(expr) - g.write('.msg') + g.write('.msg))') return } if exp_sym.kind == .interface_ && got_type_raw.idx() != expected_type.idx() @@ -2801,7 +2796,7 @@ fn (mut g Gen) expr(node ast.Expr) { g.map_init(node) } ast.None { - g.write('(Option2){.state = 1, .err = (Error){.msg = _SLIT(""), .code = 0,}}') + g.write('_const_none__') } ast.OrExpr { // this should never appear here @@ -2852,7 +2847,7 @@ fn (mut g Gen) expr(node ast.Expr) { if gen_or { opt_elem_type := g.typ(elem_type.set_flag(.optional)) g.register_chan_pop_optional_call(opt_elem_type, styp) - g.write('$opt_elem_type $tmp_opt = __Option2_${styp}_popval(') + g.write('$opt_elem_type $tmp_opt = __Option3_${styp}_popval(') } else { g.write('__${styp}_popval(') } @@ -3409,7 +3404,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { if gen_or { elem_styp := g.typ(elem_type) g.register_chan_push_optional_call(elem_styp, styp) - g.write('Option2_void $tmp_opt = __Option2_${styp}_pushval(') + g.write('Option3_void $tmp_opt = __Option3_${styp}_pushval(') } else { g.write('__${styp}_pushval(') } @@ -3719,9 +3714,13 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str g.write('${dot_or_ptr}_typ == ') g.expr(branch.exprs[sumtype_index]) } else if sym.kind == .interface_ { - typ := branch.exprs[sumtype_index] as ast.Type - branch_sym := g.table.get_type_symbol(typ.typ) - g.write('${dot_or_ptr}_interface_idx == _${sym.cname}_${branch_sym.cname}_index') + if branch.exprs[sumtype_index] is ast.Type { + typ := branch.exprs[sumtype_index] as ast.Type + branch_sym := g.table.get_type_symbol(typ.typ) + g.write('${dot_or_ptr}_interface_idx == _${sym.cname}_${branch_sym.cname}_index') + } else if branch.exprs[sumtype_index] is ast.None && sym.name == 'IError' { + g.write('${dot_or_ptr}_interface_idx == _IError_None___index') + } } if is_expr && tmp_var.len == 0 { g.write(') ? ') @@ -4227,7 +4226,7 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { // define `err` only for simple `if val := opt {...} else {` if is_guard && guard_idx == i - 1 { cvar_name := guard_vars[guard_idx] - g.writeln('\tError err = ${cvar_name}.err;') + g.writeln('\tIError err = ${cvar_name}.err;') } } else { match branch.cond { @@ -4344,27 +4343,17 @@ fn (mut g Gen) return_statement(node ast.Return) { ftyp := g.typ(node.types[0]) mut is_regular_option := ftyp in ['Option2', 'Option3'] if optional_none || is_regular_option { - tmp := g.new_tmp_var() - g.write('Option2 $tmp = ') - g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) - g.writeln(';') styp := g.typ(g.fn_decl.return_type) - err_obj := g.new_tmp_var() - g.writeln('$styp $err_obj;') - g.writeln('memcpy(&$err_obj, &$tmp, sizeof(Option2));') - g.writeln('return $err_obj;') + g.write('return ($styp){ .state=2, .err=') + g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) + g.writeln(' };') return } else if node.types[0] == table.error_type_idx { // foo() or { return err } - tmp := g.new_tmp_var() - g.write('Option2 $tmp = (Option2){.state=2, .err=') - g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) - g.writeln('};') styp := g.typ(g.fn_decl.return_type) - err_obj := g.new_tmp_var() - g.writeln('$styp $err_obj;') - g.writeln('memcpy(&$err_obj, &$tmp, sizeof(Option2));') - g.writeln('return $err_obj;') + g.write('return ($styp){.state=2, .err=') + g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) + g.writeln(' };') return } } @@ -4381,7 +4370,7 @@ fn (mut g Gen) return_statement(node ast.Return) { opt_tmp = g.new_tmp_var() g.writeln('$opt_type $opt_tmp;') styp = g.base_type(g.fn_decl.return_type) - g.write('opt_ok(&($styp/*X*/[]) { ') + g.write('opt_ok3(&($styp/*X*/[]) { ') } else { g.write('return ') styp = g.typ(g.fn_decl.return_type) @@ -4439,7 +4428,7 @@ fn (mut g Gen) return_statement(node ast.Return) { } g.write('}') if fn_return_is_optional { - g.writeln(' }, (Option2*)(&$opt_tmp), sizeof($styp));') + g.writeln(' }, (Option3*)(&$opt_tmp), sizeof($styp));') g.write('return $opt_tmp') } // Make sure to add our unpacks @@ -4465,7 +4454,7 @@ fn (mut g Gen) return_statement(node ast.Return) { // Create a tmp for this option opt_tmp := g.new_tmp_var() g.writeln('$opt_type $opt_tmp;') - g.write('opt_ok(&($styp[]) { ') + g.write('opt_ok3(&($styp[]) { ') if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() { if !(node.exprs[0] is ast.Ident && !g.is_amp) { g.write('*') @@ -4477,7 +4466,7 @@ fn (mut g Gen) return_statement(node ast.Return) { g.write(', ') } } - g.writeln(' }, (Option2*)(&$opt_tmp), sizeof($styp));') + g.writeln(' }, (Option3*)(&$opt_tmp), sizeof($styp));') g.writeln('return $opt_tmp;') return } @@ -4585,7 +4574,7 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) { } } ast.CallExpr { - if val.starts_with('Option2_') { + if val.starts_with('Option3_') { g.inits[field.mod].writeln(val) unwrap_option := field.expr.or_block.kind != .absent g.const_decl_init_later(field.mod, name, g.current_tmp_var(), field.typ, @@ -4614,6 +4603,9 @@ fn (mut g Gen) const_decl_init_later(mod string, name string, val string, typ ta // Initialize more complex consts in `void _vinit/2{}` // (C doesn't allow init expressions that can't be resolved at compile time). mut styp := g.typ(typ) + if styp == 'Option2' { + styp = 'IError' + } cname := '_const_$name' g.definitions.writeln('$styp $cname; // inited later') if cname == '_const_os__args' { @@ -5268,7 +5260,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table. is_none_ok := mr_styp == 'void' g.writeln(';') if is_none_ok { - g.writeln('if (${cvar_name}.state == 2) {') + g.writeln('if (${cvar_name}.state != 0 && ${cvar_name}.err._interface_idx != _IError_None___index) {') } else { g.writeln('if (${cvar_name}.state != 0) { /*or block*/ ') } @@ -5276,7 +5268,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table. if g.inside_or_block { g.writeln('\terr = ${cvar_name}.err;') } else { - g.writeln('\tError err = ${cvar_name}.err;') + g.writeln('\tIError err = ${cvar_name}.err;') } g.inside_or_block = true defer { @@ -5312,7 +5304,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table. // In main(), an `opt()?` call is sugar for `opt() or { panic(err) }` if g.pref.is_debug { paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) - g.writeln('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ${cvar_name}.err.msg );') + g.writeln('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *${cvar_name}.err.msg );') } else { g.writeln('\tv_panic(_STR("optional not set (%.*s\\000)", 2, ${cvar_name}.err.msg));') } @@ -5331,7 +5323,7 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table. styp := g.typ(g.fn_decl.return_type) err_obj := g.new_tmp_var() g.writeln('\t$styp $err_obj;') - g.writeln('\tmemcpy(&$err_obj, &$cvar_name, sizeof(Option2));') + g.writeln('\tmemcpy(&$err_obj, &$cvar_name, sizeof(Option3));') g.writeln('\treturn $err_obj;') } } @@ -5782,8 +5774,12 @@ fn (mut g Gen) is_expr(node ast.InfixExpr) { if sym.kind == .interface_ { g.write('_interface_idx $eq ') // `_Animal_Dog_index` - sub_type := node.right as ast.Type - sub_sym := g.table.get_type_symbol(sub_type.typ) + sub_type := match mut node.right { + ast.Type { node.right.typ } + ast.None { g.table.type_idxs['None__'] } + else { table.Type(0) } + } + sub_sym := g.table.get_type_symbol(sub_type) g.write('_${c_name(sym.name)}_${c_name(sub_sym.name)}_index') return } else if sym.kind == .sum_type { diff --git a/vlib/v/gen/c/cmain.v b/vlib/v/gen/c/cmain.v index b8aca0d0f8..f3d739ea3e 100644 --- a/vlib/v/gen/c/cmain.v +++ b/vlib/v/gen/c/cmain.v @@ -141,7 +141,7 @@ pub fn (mut g Gen) gen_failing_error_propagation_for_test_fn(or_block ast.OrExpr // `or { cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg) }` // and the test is considered failed paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) - g.writeln('\tmain__cb_propagate_test_error($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ${cvar_name}.err.msg );') + g.writeln('\tmain__cb_propagate_test_error($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *(${cvar_name}.err.msg) );') g.writeln('\tg_test_fails++;') g.writeln('\tlongjmp(g_jump_buffer, 1);') } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 9d7cd99088..b1ee1eb78d 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -720,10 +720,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { } mut name := node.name if node.name == 'error' { - name = 'error2' + name = 'error3' } if node.name == 'error_with_code' { - name = 'error_with_code2' + name = 'error_with_code3' } is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic'] print_method := name @@ -773,7 +773,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { g.is_js_call = false g.writeln(');') tmp2 = g.new_tmp_var() - g.writeln('Option2_$typ $tmp2 = $fn_name ($json_obj);') + g.writeln('Option3_$typ $tmp2 = $fn_name ($json_obj);') } if !g.is_autofree { g.write('cJSON_Delete($json_obj); //del') diff --git a/vlib/v/gen/c/index.v b/vlib/v/gen/c/index.v index a893fd4fdb..3c7f263673 100644 --- a/vlib/v/gen/c/index.v +++ b/vlib/v/gen/c/index.v @@ -247,7 +247,7 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym table.TypeSymbol) { g.writeln('if ($tmp_opt_ptr) {') g.writeln('\t*(($elem_type_str*)&${tmp_opt}.data) = *(($elem_type_str*)$tmp_opt_ptr);') g.writeln('} else {') - g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = (Error){.msg=_SLIT("array index out of range"), .code=0};') + g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = error3(_SLIT("array index out of range"));') g.writeln('}') if !node.is_option { g.or_block(tmp_opt, node.or_expr, elem_type) @@ -414,8 +414,7 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym table.TypeSymbol) { g.writeln('if ($tmp_opt_ptr) {') g.writeln('\t*(($elem_type_str*)&${tmp_opt}.data) = *(($elem_type_str*)$tmp_opt_ptr);') g.writeln('} else {') - g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = (Error){.msg=_SLIT("array index out of range"), .code=0};') - + g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = error3(_SLIT("array index out of range"));') g.writeln('}') if !node.is_option { g.or_block(tmp_opt, node.or_expr, elem_type) diff --git a/vlib/v/gen/c/json.v b/vlib/v/gen/c/json.v index c92ce46358..18a59d6189 100644 --- a/vlib/v/gen/c/json.v +++ b/vlib/v/gen/c/json.v @@ -41,7 +41,7 @@ fn (mut g Gen) gen_json_for_type(typ table.Type) { dec_fn_name := js_dec_name(styp) // Make sure that this optional type actually exists g.register_optional(utyp) - dec_fn_dec := 'Option2_$styp ${dec_fn_name}(cJSON* root)' + dec_fn_dec := 'Option3_$styp ${dec_fn_name}(cJSON* root)' dec.writeln(' $dec_fn_dec { $styp res; @@ -50,8 +50,7 @@ $dec_fn_dec { if (error_ptr != NULL) { // fprintf(stderr, "Error in decode() for $styp error_ptr=: %s\\n", error_ptr); // printf("\\nbad js=%%s\\n", js.str); - Option2 err = error2(tos2(error_ptr)); - return *(Option2_$styp *)&err; + return (Option3_$styp){.state = 2,.err = error3(tos2(error_ptr))}; } } ') @@ -102,8 +101,8 @@ $enc_fn_dec { } // cJSON_delete // p.cgen.fns << '$dec return opt_ok(res); \n}' - dec.writeln('\tOption2_$styp ret;') - dec.writeln('\topt_ok(&res, (Option2*)&ret, sizeof(res));') + dec.writeln('\tOption3_$styp ret;') + dec.writeln('\topt_ok3(&res, (Option3*)&ret, sizeof(res));') dec.writeln('\treturn ret;\n}') enc.writeln('\treturn o;\n}') g.definitions.writeln(dec.str()) @@ -152,18 +151,18 @@ fn (mut g Gen) gen_struct_enc_dec(type_info table.TypeInfo, styp string, mut enc } else { g.gen_json_for_type(field.typ) tmp := g.new_tmp_var() - dec.writeln('\tOption2_$field_type $tmp = $dec_name (js_get(root,"$name"));') + dec.writeln('\tOption3_$field_type $tmp = $dec_name (js_get(root,"$name"));') dec.writeln('\tif(${tmp}.state != 0) {') - dec.writeln('\t\treturn *(Option2_$styp*) &$tmp;') + dec.writeln('\t\treturn *(Option3_$styp*) &$tmp;') dec.writeln('\t}') dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;') } } else { // dec.writeln(' $dec_name (js_get(root, "$name"), & (res . $field.name));') tmp := g.new_tmp_var() - dec.writeln('\tOption2_$field_type $tmp = $dec_name (js_get(root,"$name"));') + dec.writeln('\tOption3_$field_type $tmp = $dec_name (js_get(root,"$name"));') dec.writeln('\tif(${tmp}.state != 0) {') - dec.writeln('\t\treturn *(Option2_$styp*) &$tmp;') + dec.writeln('\t\treturn *(Option3_$styp*) &$tmp;') dec.writeln('\t}') dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;') } @@ -215,18 +214,17 @@ fn (mut g Gen) decode_array(value_type table.Type) string { s = '$styp val = ${fn_name}(jsval); ' } else { s = ' - Option2_$styp val2 = $fn_name (jsval); + Option3_$styp val2 = $fn_name (jsval); if(val2.state != 0) { array_free(&res); - return *(Option2_Array_$styp*)&val2; + return *(Option3_Array_$styp*)&val2; } $styp val = *($styp*)val2.data; ' } return ' if(root && !cJSON_IsArray(root) && !cJSON_IsNull(root)) { - Option2 err = error2( string_add(_SLIT("Json element is not an array: "), tos2(cJSON_PrintUnformatted(root))) ); - return *(Option2_Array_$styp *)&err; + return (Option3_Array_$styp){.state = 2, .err = error3(string_add(_SLIT("Json element is not an array: "), tos2(cJSON_PrintUnformatted(root))))}; } res = __new_array(0, 0, sizeof($styp)); const cJSON *jsval = NULL; @@ -260,18 +258,17 @@ fn (mut g Gen) decode_map(key_type table.Type, value_type table.Type) string { s = '$styp_v val = $fn_name_v (js_get(root, jsval->string));' } else { s = ' - Option2_$styp_v val2 = $fn_name_v (js_get(root, jsval->string)); + Option3_$styp_v val2 = $fn_name_v (js_get(root, jsval->string)); if(val2.state != 0) { map_free(&res); - return *(Option2_Map_${styp}_$styp_v*)&val2; + return *(Option3_Map_${styp}_$styp_v*)&val2; } $styp_v val = *($styp_v*)val2.data; ' } return ' if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) { - Option2 err = error2( string_add(_SLIT("Json element is not an object: "), tos2(cJSON_PrintUnformatted(root))) ); - return *(Option2_Map_${styp}_$styp_v *)&err; + return (Option3_Map_${styp}_$styp_v){ .state = 2, .err = error3( string_add(_SLIT("Json element is not an object: "), tos2(cJSON_PrintUnformatted(root))) )}; } res = new_map_2(sizeof($styp), sizeof($styp_v), $hash_fn, $key_eq_fn, $clone_fn, $free_fn); cJSON *jsval = NULL; diff --git a/vlib/v/gen/js/tests/js.v b/vlib/v/gen/js/tests/js.v index b8d9a6bd1e..0722e9f5d2 100644 --- a/vlib/v/gen/js/tests/js.v +++ b/vlib/v/gen/js/tests/js.v @@ -88,7 +88,7 @@ fn main() { println(message) }) hl.raw_js_log() - propagation() or { println(err.msg) } + propagation() or { println(err) } } fn anon_consumer(greeting string, anon fn (message string)) { diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v index bdb01e3a6f..65c906e081 100644 --- a/vlib/v/markused/markused.v +++ b/vlib/v/markused/markused.v @@ -30,7 +30,8 @@ pub fn mark_used(mut the_table table.Table, pref &pref.Preferences, ast_files [] 'tos2', 'tos3', 'isnil', - 'opt_ok', + 'opt_ok3', + 'error3', // utf8_str_visible_length is used by c/str.v 'utf8_str_visible_length', 'compare_ints', diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index c55a7c6144..cd6f71aec7 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -235,9 +235,7 @@ fn (mut w Walker) expr(node ast.Expr) { w.stmts(b.stmts) } } - ast.None { - w.mark_fn_as_used('opt_none2') - } + ast.None {} ast.ParExpr { w.expr(node.expr) } diff --git a/vlib/v/table/types.v b/vlib/v/table/types.v index 22390896ed..23650936f1 100644 --- a/vlib/v/table/types.v +++ b/vlib/v/table/types.v @@ -551,7 +551,7 @@ pub fn (mut t Table) register_builtin_type_symbols() { return_type: table.void_type } ) - t.register_type_symbol(kind: .struct_, name: 'Error', cname: 'Error', mod: 'builtin') + t.register_type_symbol(kind: .interface_, name: 'IError', cname: 'IError', mod: 'builtin') } [inline] diff --git a/vlib/v/tests/str_gen_test.v b/vlib/v/tests/str_gen_test.v index 66e4241555..0a50c3db35 100644 --- a/vlib/v/tests/str_gen_test.v +++ b/vlib/v/tests/str_gen_test.v @@ -296,7 +296,12 @@ fn create_option_err() ?string { } fn test_option_err() { - assert '$create_option_err()' == 'Option(error: \'this is an error\')' + assert '$create_option_err()' == " +Option(error: IError(Error{ + msg: 'this is an error' + code: 0 +})) +".trim_space() } fn create_option_none() ?string { @@ -304,7 +309,7 @@ fn create_option_none() ?string { } fn test_option_none() { - assert '$create_option_none()' == 'Option(none)' + assert '$create_option_none()' == 'Option(error: IError(none))' } fn create_option_string() ?string {