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
}
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 {

View File

@ -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 {

View File

@ -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++

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 {
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

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.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('}')

View File

@ -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_ {
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 {

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) }`
// 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);')
}

View File

@ -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')

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('\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)

View File

@ -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;

View File

@ -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)) {

View File

@ -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',

View File

@ -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)
}

View File

@ -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]

View File

@ -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 {