all: implement error interfaces (#9291)

pull/9295/head
spaceface 2021-03-13 18:13:50 +01:00 committed by GitHub
parent 167dcc415d
commit e9797c618a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 143 additions and 145 deletions

View File

@ -207,7 +207,7 @@ fn (vd VDoc) get_readme(path string) string {
return readme_contents return readme_contents
} }
fn (vd VDoc) emit_generate_err(err Error) { fn (vd VDoc) emit_generate_err(err IError) {
cfg := vd.cfg cfg := vd.cfg
mut err_msg := err.msg mut err_msg := err.msg
if err.code == 1 { if err.code == 1 {

View File

@ -16,16 +16,20 @@ pub:
code int code int
} }
pub struct Option3 { struct Option3 {
state byte state byte
err IError err IError = none__
} }
[inline] const none__ = IError(&None__{})
fn (e IError) str() string {
return e.msg struct None__ {
msg string
code int
} }
fn (_ None__) str() string { return 'none' }
fn opt_ok3(data voidptr, mut option Option3, size int) { fn opt_ok3(data voidptr, mut option Option3, size int) {
unsafe { unsafe {
*option = Option3{} *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] [inline]
pub fn error3(message string) IError { pub fn error3(message string) IError {
return &Error{ return &Error{
@ -73,13 +67,6 @@ struct Option2 {
// derived Option2_xxx types // 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 foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
fn opt_ok(data voidptr, mut option Option2, size int) { fn opt_ok(data voidptr, mut option Option2, size int) {
unsafe { 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`. // error returns an optional containing the error given in `message`.
// `if ouch { return error('an error occurred') }` // `if ouch { return error('an error occurred') }`
pub fn error2(message string) Option2 { pub fn error2(message string) Option2 {

View File

@ -14,7 +14,7 @@ fn do_rec_calc_send(chs []chan i64, mut sem sync.Semaphore) {
mut msg := '' mut msg := ''
for { for {
mut s := get_val_from_chan(chs[0]) or { mut s := get_val_from_chan(chs[0]) or {
msg = err.str() msg = err.msg
break break
} }
s++ s++

View File

@ -413,7 +413,7 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
c.check_expected(field_expr_type, field.typ) or { c.check_expected(field_expr_type, field.typ) or {
if !(sym.kind == .interface_ if !(sym.kind == .interface_
&& c.type_implements(field_expr_type, field.typ, field.pos)) { && 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()) 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) expr_type_sym := c.table.get_type_symbol(expr_type)
if expr_type != table.void_type && expr_type_sym.kind != .placeholder { if expr_type != table.void_type && expr_type_sym.kind != .placeholder {
c.check_expected(expr_type, embed_type) or { 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) 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) c.type_implements(expr_type, info_field.typ, field.pos)
} else if expr_type != table.void_type && expr_type_sym.kind != .placeholder { } else if expr_type != table.void_type && expr_type_sym.kind != .placeholder {
c.check_expected(expr_type, info_field.typ) or { 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) 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 elem_type := right.array_info().elem_type
// if left_default.kind != right_sym.kind { // if left_default.kind != right_sym.kind {
c.check_expected(left_type, elem_type) or { 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) left_right_pos)
} }
} }
.map { .map {
map_info := right.map_info() map_info := right.map_info()
c.check_expected(left_type, map_info.key_type) or { 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) left_right_pos)
} }
infix_expr.left_type = map_info.key_type infix_expr.left_type = map_info.key_type
} }
.string { .string {
c.check_expected(left_type, right_type) or { 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) 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) return c.check_shift(left_type, right_type, left_pos, right_pos)
} }
.key_is, .not_is { .key_is, .not_is {
type_expr := infix_expr.right as ast.Type right_expr := infix_expr.right
typ_sym := c.table.get_type_symbol(type_expr.typ) 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 { 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] { if left.kind !in [.interface_, .sum_type] {
c.error('`$infix_expr.op.str()` can only be used with interfaces and sum types', c.error('`$op` can only be used with interfaces and sum types', infix_expr.pos)
infix_expr.pos)
} else if mut left.info is table.SumType { } 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) 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 { if left_type_sym.kind == .aggregate {
// the error message contains the problematic type // the error message contains the problematic type
unknown_method_msg = err unknown_method_msg = err.msg
} }
} }
if has_method { if has_method {
@ -1493,7 +1505,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
// continue // continue
// } // }
if got_arg_typ != table.void_type { 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) 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 { if f.generic_names.len > 0 {
continue 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 { 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) utyp := c.unwrap_generic(typ)
typ_sym := c.table.get_type_symbol(utyp) typ_sym := c.table.get_type_symbol(utyp)
mut inter_sym := c.table.get_type_symbol(inter_typ) 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) styp := c.table.type_to_str(utyp)
same_base_type := utyp.idx() == inter_typ.idx() if utyp.idx() == inter_typ.idx() {
if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ && !same_base_type { // 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`', c.error('cannot implement interface `$inter_sym.name` with a different interface `$styp`',
pos) 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] { if sym.kind in [.aggregate, .sum_type] {
unknown_field_msg = err unknown_field_msg = err.msg
} }
} }
if !c.inside_unsafe { if !c.inside_unsafe {
@ -2884,7 +2911,7 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
&& left_sym.kind != .interface_ { && left_sym.kind != .interface_ {
// Dual sides check (compatibility check) // Dual sides check (compatibility check)
c.check_expected(right_type_unwrapped, left_type_unwrapped) or { 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_ { if left_sym.kind == .interface_ {
@ -3064,7 +3091,7 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type {
continue continue
} }
c.check_expected(typ, elem_type) or { 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 { 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 // probably any mismatch will be caught by not producing a value instead
for st in branch.stmts[0..branch.stmts.len - 1] { for st in branch.stmts[0..branch.stmts.len - 1] {
// must not contain C statements // 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 // 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 { for st in branch.stmts {
// must not contain C statements // 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 // Also check for returns inside a comp.if's statements, even if its contents aren't parsed

View File

@ -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.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('string indent_${str_fn_name}($styp it, int indent_count) {')
g.auto_str_funcs.writeln('\tstring res;') g.auto_str_funcs.writeln('\tstring res;')
g.auto_str_funcs.writeln('\tif (it.state == 1) {') g.auto_str_funcs.writeln('\tif (it.state == 0) {')
g.auto_str_funcs.writeln('\t\tres = _SLIT("none");')
g.auto_str_funcs.writeln('\t} else if (it.state == 0) {')
if sym.kind == .string { if sym.kind == .string {
g.auto_str_funcs.writeln('\t\tres = _STR("\'%.*s\\000\'", 2, ${parent_str_fn_name}(*($sym.cname*)it.data));') 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 { } 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\tres = ${parent_str_fn_name}(*($sym.cname*)it.data);')
} }
g.auto_str_funcs.writeln('\t} else {') 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('\t}')
g.auto_str_funcs.writeln('\treturn _STR("Option(%.*s\\000)", 2, res);') g.auto_str_funcs.writeln('\treturn _STR("Option(%.*s\\000)", 2, res);')
g.auto_str_funcs.writeln('}') g.auto_str_funcs.writeln('}')

View File

@ -569,7 +569,7 @@ fn (mut g Gen) expr_string(expr ast.Expr) string {
// if one location changes // if one location changes
fn (mut g Gen) optional_type_name(t table.Type) (string, string) { fn (mut g Gen) optional_type_name(t table.Type) (string, string) {
base := g.base_type(t) base := g.base_type(t)
mut styp := 'Option2_$base' mut styp := 'Option3_$base'
if t.is_ptr() { if t.is_ptr() {
styp = styp.replace('*', '_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 } size := if base == 'void' { 'byte' } else { base }
ret := 'struct $styp { ret := 'struct $styp {
byte state; byte state;
Error err; IError err;
byte data[sizeof($size)]; byte data[sizeof($size)];
}' }'
return ret 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 { if opt_el_type !in g.chan_pop_optionals {
g.chan_pop_optionals << opt_el_type g.chan_pop_optionals << opt_el_type
g.channel_definitions.writeln(' 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}; $opt_el_type _tmp = {0};
if (sync__Channel_try_pop_priv(ch, _tmp.data, false)) { if (sync__Channel_try_pop_priv(ch, _tmp.data, false)) {
Option2 _tmp2 = error2(_SLIT("channel closed")); return ($opt_el_type){ .state = 2, .err = error3(_SLIT("channel closed")) };
$opt_el_type _tmp3;
memcpy(&_tmp3, &_tmp2, sizeof(Option2));
return _tmp3;
} }
return _tmp; 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 { if styp !in g.chan_push_optionals {
g.chan_push_optionals << styp g.chan_push_optionals << styp
g.channel_definitions.writeln(' 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)) { if (sync__Channel_try_push_priv(ch, &e, false)) {
Option2 _tmp2 = error2(_SLIT("channel closed")); return (Option3_void){ .state = 2, .err = error3(_SLIT("channel closed")) };
Option2_void _tmp3;
memcpy(&_tmp3, &_tmp2, sizeof(Option2));
return _tmp3;
} }
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) sym := g.table.get_type_symbol(stmt.typ)
if sym.name in ['Option2', 'Option3'] || stmt.expr is ast.None { if sym.name in ['Option2', 'Option3'] || stmt.expr is ast.None {
tmp := g.new_tmp_var() tmp := g.new_tmp_var()
g.write('Option2 $tmp = ') g.write('Option3 $tmp = (Option3){.state = 0,.err = ')
g.expr(stmt.expr) g.expr(stmt.expr)
g.writeln(';') g.writeln('};')
g.writeln('memcpy(&$tmp_var, &$tmp, sizeof(Option2));') g.writeln('memcpy(&$tmp_var, &$tmp, sizeof(Option3));')
} else { } else {
mut styp := g.base_type(stmt.typ) mut styp := g.base_type(stmt.typ)
$if tinyc && x32 && windows { $if tinyc && x32 && windows {
@ -926,9 +920,9 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) {
styp = 'f64' styp = 'f64'
} }
} }
g.write('opt_ok(&($styp[]) { ') g.write('opt_ok3(&($styp[]) { ')
g.stmt(stmt) g.stmt(stmt)
g.writeln(' }, (Option2*)(&$tmp_var), sizeof($styp));') g.writeln(' }, (Option3*)(&$tmp_var), sizeof($styp));')
} }
} }
} else { } 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 // 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 // TODO: temporary to allow people to migrate their code; remove soon
if got_type == table.error_type_idx && expected_type == table.string_type_idx { if got_type == table.error_type_idx && expected_type == table.string_type_idx {
g.write('(*(')
g.expr(expr) g.expr(expr)
g.write('.msg') g.write('.msg))')
return return
} }
if exp_sym.kind == .interface_ && got_type_raw.idx() != expected_type.idx() 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) g.map_init(node)
} }
ast.None { ast.None {
g.write('(Option2){.state = 1, .err = (Error){.msg = _SLIT(""), .code = 0,}}') g.write('_const_none__')
} }
ast.OrExpr { ast.OrExpr {
// this should never appear here // this should never appear here
@ -2852,7 +2847,7 @@ fn (mut g Gen) expr(node ast.Expr) {
if gen_or { if gen_or {
opt_elem_type := g.typ(elem_type.set_flag(.optional)) opt_elem_type := g.typ(elem_type.set_flag(.optional))
g.register_chan_pop_optional_call(opt_elem_type, styp) 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 { } else {
g.write('__${styp}_popval(') g.write('__${styp}_popval(')
} }
@ -3409,7 +3404,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
if gen_or { if gen_or {
elem_styp := g.typ(elem_type) elem_styp := g.typ(elem_type)
g.register_chan_push_optional_call(elem_styp, styp) 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 { } else {
g.write('__${styp}_pushval(') 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.write('${dot_or_ptr}_typ == ')
g.expr(branch.exprs[sumtype_index]) g.expr(branch.exprs[sumtype_index])
} else if sym.kind == .interface_ { } else if sym.kind == .interface_ {
typ := branch.exprs[sumtype_index] as ast.Type if branch.exprs[sumtype_index] is ast.Type {
branch_sym := g.table.get_type_symbol(typ.typ) typ := branch.exprs[sumtype_index] as ast.Type
g.write('${dot_or_ptr}_interface_idx == _${sym.cname}_${branch_sym.cname}_index') 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 { if is_expr && tmp_var.len == 0 {
g.write(') ? ') g.write(') ? ')
@ -4227,7 +4226,7 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
// define `err` only for simple `if val := opt {...} else {` // define `err` only for simple `if val := opt {...} else {`
if is_guard && guard_idx == i - 1 { if is_guard && guard_idx == i - 1 {
cvar_name := guard_vars[guard_idx] cvar_name := guard_vars[guard_idx]
g.writeln('\tError err = ${cvar_name}.err;') g.writeln('\tIError err = ${cvar_name}.err;')
} }
} else { } else {
match branch.cond { match branch.cond {
@ -4344,27 +4343,17 @@ fn (mut g Gen) return_statement(node ast.Return) {
ftyp := g.typ(node.types[0]) ftyp := g.typ(node.types[0])
mut is_regular_option := ftyp in ['Option2', 'Option3'] mut is_regular_option := ftyp in ['Option2', 'Option3']
if optional_none || is_regular_option { 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) styp := g.typ(g.fn_decl.return_type)
err_obj := g.new_tmp_var() g.write('return ($styp){ .state=2, .err=')
g.writeln('$styp $err_obj;') g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
g.writeln('memcpy(&$err_obj, &$tmp, sizeof(Option2));') g.writeln(' };')
g.writeln('return $err_obj;')
return return
} else if node.types[0] == table.error_type_idx { } else if node.types[0] == table.error_type_idx {
// foo() or { return err } // 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) styp := g.typ(g.fn_decl.return_type)
err_obj := g.new_tmp_var() g.write('return ($styp){.state=2, .err=')
g.writeln('$styp $err_obj;') g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
g.writeln('memcpy(&$err_obj, &$tmp, sizeof(Option2));') g.writeln(' };')
g.writeln('return $err_obj;')
return return
} }
} }
@ -4381,7 +4370,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
opt_tmp = g.new_tmp_var() opt_tmp = g.new_tmp_var()
g.writeln('$opt_type $opt_tmp;') g.writeln('$opt_type $opt_tmp;')
styp = g.base_type(g.fn_decl.return_type) styp = g.base_type(g.fn_decl.return_type)
g.write('opt_ok(&($styp/*X*/[]) { ') g.write('opt_ok3(&($styp/*X*/[]) { ')
} else { } else {
g.write('return ') g.write('return ')
styp = g.typ(g.fn_decl.return_type) styp = g.typ(g.fn_decl.return_type)
@ -4439,7 +4428,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
} }
g.write('}') g.write('}')
if fn_return_is_optional { if fn_return_is_optional {
g.writeln(' }, (Option2*)(&$opt_tmp), sizeof($styp));') g.writeln(' }, (Option3*)(&$opt_tmp), sizeof($styp));')
g.write('return $opt_tmp') g.write('return $opt_tmp')
} }
// Make sure to add our unpacks // 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 // Create a tmp for this option
opt_tmp := g.new_tmp_var() opt_tmp := g.new_tmp_var()
g.writeln('$opt_type $opt_tmp;') 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 !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() {
if !(node.exprs[0] is ast.Ident && !g.is_amp) { if !(node.exprs[0] is ast.Ident && !g.is_amp) {
g.write('*') g.write('*')
@ -4477,7 +4466,7 @@ fn (mut g Gen) return_statement(node ast.Return) {
g.write(', ') g.write(', ')
} }
} }
g.writeln(' }, (Option2*)(&$opt_tmp), sizeof($styp));') g.writeln(' }, (Option3*)(&$opt_tmp), sizeof($styp));')
g.writeln('return $opt_tmp;') g.writeln('return $opt_tmp;')
return return
} }
@ -4585,7 +4574,7 @@ fn (mut g Gen) const_decl(node ast.ConstDecl) {
} }
} }
ast.CallExpr { ast.CallExpr {
if val.starts_with('Option2_') { if val.starts_with('Option3_') {
g.inits[field.mod].writeln(val) g.inits[field.mod].writeln(val)
unwrap_option := field.expr.or_block.kind != .absent unwrap_option := field.expr.or_block.kind != .absent
g.const_decl_init_later(field.mod, name, g.current_tmp_var(), field.typ, 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{}` // Initialize more complex consts in `void _vinit/2{}`
// (C doesn't allow init expressions that can't be resolved at compile time). // (C doesn't allow init expressions that can't be resolved at compile time).
mut styp := g.typ(typ) mut styp := g.typ(typ)
if styp == 'Option2' {
styp = 'IError'
}
cname := '_const_$name' cname := '_const_$name'
g.definitions.writeln('$styp $cname; // inited later') g.definitions.writeln('$styp $cname; // inited later')
if cname == '_const_os__args' { 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' is_none_ok := mr_styp == 'void'
g.writeln(';') g.writeln(';')
if is_none_ok { 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 { } else {
g.writeln('if (${cvar_name}.state != 0) { /*or block*/ ') 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 { if g.inside_or_block {
g.writeln('\terr = ${cvar_name}.err;') g.writeln('\terr = ${cvar_name}.err;')
} else { } else {
g.writeln('\tError err = ${cvar_name}.err;') g.writeln('\tIError err = ${cvar_name}.err;')
} }
g.inside_or_block = true g.inside_or_block = true
defer { 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) }` // In main(), an `opt()?` call is sugar for `opt() or { panic(err) }`
if g.pref.is_debug { if g.pref.is_debug {
paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) 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 { } else {
g.writeln('\tv_panic(_STR("optional not set (%.*s\\000)", 2, ${cvar_name}.err.msg));') 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) styp := g.typ(g.fn_decl.return_type)
err_obj := g.new_tmp_var() err_obj := g.new_tmp_var()
g.writeln('\t$styp $err_obj;') 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;') g.writeln('\treturn $err_obj;')
} }
} }
@ -5782,8 +5774,12 @@ fn (mut g Gen) is_expr(node ast.InfixExpr) {
if sym.kind == .interface_ { if sym.kind == .interface_ {
g.write('_interface_idx $eq ') g.write('_interface_idx $eq ')
// `_Animal_Dog_index` // `_Animal_Dog_index`
sub_type := node.right as ast.Type sub_type := match mut node.right {
sub_sym := g.table.get_type_symbol(sub_type.typ) 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') g.write('_${c_name(sym.name)}_${c_name(sub_sym.name)}_index')
return return
} else if sym.kind == .sum_type { } else if sym.kind == .sum_type {

View File

@ -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) }` // `or { cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg) }`
// and the test is considered failed // and the test is considered failed
paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) 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('\tg_test_fails++;')
g.writeln('\tlongjmp(g_jump_buffer, 1);') g.writeln('\tlongjmp(g_jump_buffer, 1);')
} }

View File

@ -720,10 +720,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
} }
mut name := node.name mut name := node.name
if node.name == 'error' { if node.name == 'error' {
name = 'error2' name = 'error3'
} }
if node.name == 'error_with_code' { if node.name == 'error_with_code' {
name = 'error_with_code2' name = 'error_with_code3'
} }
is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic'] is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic']
print_method := name print_method := name
@ -773,7 +773,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
g.is_js_call = false g.is_js_call = false
g.writeln(');') g.writeln(');')
tmp2 = g.new_tmp_var() 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 { if !g.is_autofree {
g.write('cJSON_Delete($json_obj); //del') g.write('cJSON_Delete($json_obj); //del')

View File

@ -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('if ($tmp_opt_ptr) {')
g.writeln('\t*(($elem_type_str*)&${tmp_opt}.data) = *(($elem_type_str*)$tmp_opt_ptr);') g.writeln('\t*(($elem_type_str*)&${tmp_opt}.data) = *(($elem_type_str*)$tmp_opt_ptr);')
g.writeln('} else {') 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('}') g.writeln('}')
if !node.is_option { if !node.is_option {
g.or_block(tmp_opt, node.or_expr, elem_type) 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('if ($tmp_opt_ptr) {')
g.writeln('\t*(($elem_type_str*)&${tmp_opt}.data) = *(($elem_type_str*)$tmp_opt_ptr);') g.writeln('\t*(($elem_type_str*)&${tmp_opt}.data) = *(($elem_type_str*)$tmp_opt_ptr);')
g.writeln('} else {') 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('}') g.writeln('}')
if !node.is_option { if !node.is_option {
g.or_block(tmp_opt, node.or_expr, elem_type) g.or_block(tmp_opt, node.or_expr, elem_type)

View File

@ -41,7 +41,7 @@ fn (mut g Gen) gen_json_for_type(typ table.Type) {
dec_fn_name := js_dec_name(styp) dec_fn_name := js_dec_name(styp)
// Make sure that this optional type actually exists // Make sure that this optional type actually exists
g.register_optional(utyp) 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.writeln('
$dec_fn_dec { $dec_fn_dec {
$styp res; $styp res;
@ -50,8 +50,7 @@ $dec_fn_dec {
if (error_ptr != NULL) { if (error_ptr != NULL) {
// fprintf(stderr, "Error in decode() for $styp error_ptr=: %s\\n", error_ptr); // fprintf(stderr, "Error in decode() for $styp error_ptr=: %s\\n", error_ptr);
// printf("\\nbad js=%%s\\n", js.str); // printf("\\nbad js=%%s\\n", js.str);
Option2 err = error2(tos2(error_ptr)); return (Option3_$styp){.state = 2,.err = error3(tos2(error_ptr))};
return *(Option2_$styp *)&err;
} }
} }
') ')
@ -102,8 +101,8 @@ $enc_fn_dec {
} }
// cJSON_delete // cJSON_delete
// p.cgen.fns << '$dec return opt_ok(res); \n}' // p.cgen.fns << '$dec return opt_ok(res); \n}'
dec.writeln('\tOption2_$styp ret;') dec.writeln('\tOption3_$styp ret;')
dec.writeln('\topt_ok(&res, (Option2*)&ret, sizeof(res));') dec.writeln('\topt_ok3(&res, (Option3*)&ret, sizeof(res));')
dec.writeln('\treturn ret;\n}') dec.writeln('\treturn ret;\n}')
enc.writeln('\treturn o;\n}') enc.writeln('\treturn o;\n}')
g.definitions.writeln(dec.str()) 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 { } else {
g.gen_json_for_type(field.typ) g.gen_json_for_type(field.typ)
tmp := g.new_tmp_var() 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('\tif(${tmp}.state != 0) {')
dec.writeln('\t\treturn *(Option2_$styp*) &$tmp;') dec.writeln('\t\treturn *(Option3_$styp*) &$tmp;')
dec.writeln('\t}') dec.writeln('\t}')
dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;') dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;')
} }
} else { } else {
// dec.writeln(' $dec_name (js_get(root, "$name"), & (res . $field.name));') // dec.writeln(' $dec_name (js_get(root, "$name"), & (res . $field.name));')
tmp := g.new_tmp_var() 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('\tif(${tmp}.state != 0) {')
dec.writeln('\t\treturn *(Option2_$styp*) &$tmp;') dec.writeln('\t\treturn *(Option3_$styp*) &$tmp;')
dec.writeln('\t}') dec.writeln('\t}')
dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;') 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); ' s = '$styp val = ${fn_name}(jsval); '
} else { } else {
s = ' s = '
Option2_$styp val2 = $fn_name (jsval); Option3_$styp val2 = $fn_name (jsval);
if(val2.state != 0) { if(val2.state != 0) {
array_free(&res); array_free(&res);
return *(Option2_Array_$styp*)&val2; return *(Option3_Array_$styp*)&val2;
} }
$styp val = *($styp*)val2.data; $styp val = *($styp*)val2.data;
' '
} }
return ' return '
if(root && !cJSON_IsArray(root) && !cJSON_IsNull(root)) { 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 (Option3_Array_$styp){.state = 2, .err = error3(string_add(_SLIT("Json element is not an array: "), tos2(cJSON_PrintUnformatted(root))))};
return *(Option2_Array_$styp *)&err;
} }
res = __new_array(0, 0, sizeof($styp)); res = __new_array(0, 0, sizeof($styp));
const cJSON *jsval = NULL; 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));' s = '$styp_v val = $fn_name_v (js_get(root, jsval->string));'
} else { } else {
s = ' 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) { if(val2.state != 0) {
map_free(&res); map_free(&res);
return *(Option2_Map_${styp}_$styp_v*)&val2; return *(Option3_Map_${styp}_$styp_v*)&val2;
} }
$styp_v val = *($styp_v*)val2.data; $styp_v val = *($styp_v*)val2.data;
' '
} }
return ' return '
if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) { if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) {
Option2 err = error2( string_add(_SLIT("Json element is not an object: "), tos2(cJSON_PrintUnformatted(root))) ); return (Option3_Map_${styp}_$styp_v){ .state = 2, .err = error3( string_add(_SLIT("Json element is not an object: "), tos2(cJSON_PrintUnformatted(root))) )};
return *(Option2_Map_${styp}_$styp_v *)&err;
} }
res = new_map_2(sizeof($styp), sizeof($styp_v), $hash_fn, $key_eq_fn, $clone_fn, $free_fn); res = new_map_2(sizeof($styp), sizeof($styp_v), $hash_fn, $key_eq_fn, $clone_fn, $free_fn);
cJSON *jsval = NULL; cJSON *jsval = NULL;

View File

@ -88,7 +88,7 @@ fn main() {
println(message) println(message)
}) })
hl.raw_js_log() hl.raw_js_log()
propagation() or { println(err.msg) } propagation() or { println(err) }
} }
fn anon_consumer(greeting string, anon fn (message string)) { fn anon_consumer(greeting string, anon fn (message string)) {

View File

@ -30,7 +30,8 @@ pub fn mark_used(mut the_table table.Table, pref &pref.Preferences, ast_files []
'tos2', 'tos2',
'tos3', 'tos3',
'isnil', 'isnil',
'opt_ok', 'opt_ok3',
'error3',
// utf8_str_visible_length is used by c/str.v // utf8_str_visible_length is used by c/str.v
'utf8_str_visible_length', 'utf8_str_visible_length',
'compare_ints', 'compare_ints',

View File

@ -235,9 +235,7 @@ fn (mut w Walker) expr(node ast.Expr) {
w.stmts(b.stmts) w.stmts(b.stmts)
} }
} }
ast.None { ast.None {}
w.mark_fn_as_used('opt_none2')
}
ast.ParExpr { ast.ParExpr {
w.expr(node.expr) w.expr(node.expr)
} }

View File

@ -551,7 +551,7 @@ pub fn (mut t Table) register_builtin_type_symbols() {
return_type: table.void_type 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] [inline]

View File

@ -296,7 +296,12 @@ fn create_option_err() ?string {
} }
fn test_option_err() { 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 { fn create_option_none() ?string {
@ -304,7 +309,7 @@ fn create_option_none() ?string {
} }
fn test_option_none() { fn test_option_none() {
assert '$create_option_none()' == 'Option(none)' assert '$create_option_none()' == 'Option(error: IError(none))'
} }
fn create_option_string() ?string { fn create_option_string() ?string {