From 75eac291ac4e578407c7feb1495c7aacf5707f5d Mon Sep 17 00:00:00 2001 From: Emily Hudson Date: Sun, 31 May 2020 11:57:26 +0100 Subject: [PATCH] all: variable sized options --- vlib/builtin/builtin.v | 2 + vlib/builtin/builtin_nix.c.v | 3 + vlib/builtin/builtin_windows.c.v | 13 +- vlib/builtin/option.v | 36 ++++- vlib/os/os.v | 7 +- vlib/os/os_nix.c.v | 4 + vlib/os/os_windows.c.v | 9 +- vlib/v/gen/cgen.v | 250 ++++++++++++++++++++----------- vlib/v/gen/json.v | 14 +- vlib/v/tests/option_test.v | 7 + 10 files changed, 251 insertions(+), 94 deletions(-) diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 845696985a..d7515cf26a 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -44,12 +44,14 @@ fn panic_debug(line_no int, file, mod, fn_name, s string) { eprintln(' line: ' + line_no.str()) eprintln('=========================================') print_backtrace_skipping_top_frames(1) + break_if_debugger_attached() C.exit(1) } pub fn panic(s string) { eprintln('V panic: $s') print_backtrace() + break_if_debugger_attached() C.exit(1) } diff --git a/vlib/builtin/builtin_nix.c.v b/vlib/builtin/builtin_nix.c.v index 844db44b3f..923125844d 100644 --- a/vlib/builtin/builtin_nix.c.v +++ b/vlib/builtin/builtin_nix.c.v @@ -121,3 +121,6 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { } return true } + +fn break_if_debugger_attached() { +} \ No newline at end of file diff --git a/vlib/builtin/builtin_windows.c.v b/vlib/builtin/builtin_windows.c.v index 8bc2412d6c..98999e9777 100644 --- a/vlib/builtin/builtin_windows.c.v +++ b/vlib/builtin/builtin_windows.c.v @@ -190,6 +190,15 @@ fn unhandled_exception_handler(e &ExceptionPointers) u32 { return 0 } -pub fn add_unhandled_exception_handler() { +fn add_unhandled_exception_handler() { add_vectored_exception_handler(unhandled_exception_handler) -} \ No newline at end of file +} + +fn C.IsDebuggerPresent() bool +fn C.__debugbreak() + +fn break_if_debugger_attached() { + if C.IsDebuggerPresent() { + C.__debugbreak() + } +} diff --git a/vlib/builtin/option.v b/vlib/builtin/option.v index 8c30117c86..db5f4d8559 100644 --- a/vlib/builtin/option.v +++ b/vlib/builtin/option.v @@ -12,12 +12,46 @@ struct Option2 { } */ +struct OptionBase { + ok bool + is_none bool + error string + ecode int + // Data is trailing after ecode + // and is not included in here but in the + // derived Option_xxx types +} + +pub fn (o OptionBase) str() string { + if o.ok && !o.is_none { + return 'Option{ valid }' + } + if o.is_none { + return 'Option{ none }' + } + return 'Option{ error: "${o.error}" }' +} + +// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }` +fn opt_ok2(data voidptr, mut option &OptionBase, size int) { + unsafe { + *option = OptionBase { + ok: true + } + } + + // use ecode to get the end of OptionBase and then memcpy into it + C.memcpy(byteptr(&option.ecode) + sizeof(int), data, size) +} + +// Old option type used for bootstrapping struct Option { ok bool is_none bool error string ecode int + data [400]byte } @@ -66,4 +100,4 @@ pub fn error_with_code(s string, code int) Option { error: s ecode: code } -} \ No newline at end of file +} diff --git a/vlib/os/os.v b/vlib/os/os.v index a571e0e60a..650652e58e 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -798,12 +798,17 @@ pub fn get_raw_stdin() []byte { for { pos := buf + offset res := C.ReadFile(h_input, pos, block_bytes, &bytes_read, 0) + offset += bytes_read + if !res { break } - offset += bytes_read + buf = v_realloc(buf, offset + block_bytes + (block_bytes-bytes_read)) } + + C.CloseHandle(h_input) + return array{element_size: 1 data: voidptr(buf) len: offset cap: offset } } } $else { diff --git a/vlib/os/os_nix.c.v b/vlib/os/os_nix.c.v index 0e1fb76230..93ba3726ab 100644 --- a/vlib/os/os_nix.c.v +++ b/vlib/os/os_nix.c.v @@ -154,3 +154,7 @@ pub fn (mut f File) close() { C.fflush(f.cfile) C.fclose(f.cfile) } + +pub fn debugger_present() bool { + return false +} diff --git a/vlib/os/os_windows.c.v b/vlib/os/os_windows.c.v index fce4bf666e..4b13feb067 100644 --- a/vlib/os/os_windows.c.v +++ b/vlib/os/os_windows.c.v @@ -345,4 +345,11 @@ pub type VectoredExceptionHandler fn(&ExceptionPointers)u32 pub fn add_vectored_exception_handler(first bool, handler VectoredExceptionHandler) { C.AddVectoredExceptionHandler(u32(first), handler) -} \ No newline at end of file +} + +// this is defined in builtin_windows.c.v in builtin +// fn C.IsDebuggerPresent() bool + +pub fn debugger_present() bool { + return C.IsDebuggerPresent() +} diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index d3762c9f25..de9304349d 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -51,6 +51,7 @@ struct Gen { includes strings.Builder // all C #includes required by V modules typedefs strings.Builder typedefs2 strings.Builder + type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) inits strings.Builder // contents of `void _vinit(){}` cleanups strings.Builder // contents of `void _vcleanup(){}` @@ -60,6 +61,7 @@ struct Gen { comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI pcs_declarations strings.Builder // -prof profile counter declarations for each function hotcode_definitions strings.Builder // -live declarations & functions + options strings.Builder // `Option_xxxx` types table &table.Table pref &pref.Preferences module_built string @@ -116,6 +118,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string includes: strings.new_builder(100) typedefs: strings.new_builder(100) typedefs2: strings.new_builder(100) + type_definitions: strings.new_builder(100) definitions: strings.new_builder(100) gowrappers: strings.new_builder(100) stringliterals: strings.new_builder(100) @@ -125,6 +128,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string cleanups: strings.new_builder(100) pcs_declarations: strings.new_builder(100) hotcode_definitions: strings.new_builder(100) + options: strings.new_builder(100) table: table pref: pref fn_decl: 0 @@ -185,6 +189,10 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string b.writeln(g.cheaders.str()) b.writeln('\n// V includes:') b.writeln(g.includes.str()) + b.writeln('\n// V type definitions:') + b.writeln(g.type_definitions.str()) + b.writeln('\n// V Option_xxx definitions:') + b.writeln(g.options.str()) b.writeln('\n// V definitions:') b.writeln(g.definitions.str()) b.writeln('\n// V profile counters:') @@ -309,20 +317,9 @@ fn (mut g Gen) typ(t table.Type) string { // T => int etc return g.typ(g.cur_generic_type) } - base := styp if t.flag_is(.optional) { - if t.is_ptr() { - styp = styp.replace('*', '_ptr') - } - styp = 'Option_' + styp // Register an optional if it's not registered yet - if styp !in g.optionals { - g.register_optional(t, base) - // println(styp) - x := styp // .replace('*', '_ptr') // handle option ptrs - g.typedefs2.writeln('typedef Option $x;') - g.optionals << styp - } + return g.register_optional(t) } /* if styp.starts_with('C__') { @@ -341,17 +338,58 @@ fn (g &Gen) base_type(t table.Type) string { return styp } -fn (mut g Gen) register_optional(t table.Type, styp string) { +// TODO this really shouldnt be seperate from typ +// but I(emily) would rather have this generation +// all unified in one place so that it doesnt break +// if one location changes +fn (g &Gen) optional_type_name(t table.Type) (string, string) { + base := g.base_type(t) + mut styp := 'Option_$base' + if t.is_ptr() { + styp = styp.replace('*', '_ptr') + } + return styp, base +} + +fn (g &Gen) optional_type_text(styp, base string) string { + x := styp // .replace('*', '_ptr') // handle option ptrs + // replace void with something else + size := if base == 'void' { + 'int' + } else { + base + } + ret := 'struct $x { + bool ok; + bool is_none; + string v_error; + int ecode; + byte data[sizeof($size)]; +} ' + return ret +} + +fn (mut g Gen) register_optional(t table.Type) string { // g.typedefs2.writeln('typedef Option $x;') - no_ptr := styp.replace('*', '_ptr') - typ := if styp == 'void' { 'void*' } else { styp } - g.hotcode_definitions.writeln('typedef struct { - $typ data; - string error; - int ecode; - bool ok; - bool is_none; - } Option2_$no_ptr;') + styp, base := g.optional_type_name(t) + if styp !in g.optionals { + no_ptr := base.replace('*', '_ptr') + typ := if base == 'void' { 'void*' } else { base } + g.hotcode_definitions.writeln('typedef struct { + $typ data; + string error; + int ecode; + bool ok; + bool is_none; + } Option2_$no_ptr;') + + // println(styp) + g.typedefs2.writeln('typedef struct $styp $styp;') + g.options.write(g.optional_type_text(styp, base)) + g.options.writeln(';\n') + g.optionals << styp + } + return styp } // cc_type returns the Cleaned Concrete Type name, *without ptr*, @@ -388,18 +426,18 @@ typedef struct { `.` parent_styp := if is_c_parent { 'struct ' + parent.name[2..].replace('.', '__') } else { parent.name.replace('.', '__') } - g.definitions.writeln('typedef $parent_styp $styp;') + g.type_definitions.writeln('typedef $parent_styp $styp;') } .array { styp := typ.name.replace('.', '__') - g.definitions.writeln('typedef array $styp;') + g.type_definitions.writeln('typedef array $styp;') } .interface_ { - g.definitions.writeln('typedef _Interface ${c_name(typ.name)};') + g.type_definitions.writeln('typedef _Interface ${c_name(typ.name)};') } .map { styp := typ.name.replace('.', '__') - g.definitions.writeln('typedef map $styp;') + g.type_definitions.writeln('typedef map $styp;') } .function { info := typ.info as table.FnType @@ -416,14 +454,14 @@ typedef struct { } else { c_name(func.name) } - g.definitions.write('typedef ${g.typ(func.return_type)} (*$fn_name)(') + g.type_definitions.write('typedef ${g.typ(func.return_type)} (*$fn_name)(') for i, arg in func.args { - g.definitions.write(g.typ(arg.typ)) + g.type_definitions.write(g.typ(arg.typ)) if i < func.args.len - 1 { - g.definitions.write(',') + g.type_definitions.write(',') } } - g.definitions.writeln(');') + g.type_definitions.writeln(');') } } else { @@ -434,7 +472,7 @@ typedef struct { } pub fn (mut g Gen) write_multi_return_types() { - g.definitions.writeln('// multi return structs') + g.type_definitions.writeln('// multi return structs') for typ in g.table.types { // sym := g.table.get_type_symbol(typ) if typ.kind != .multi_return { @@ -442,30 +480,30 @@ pub fn (mut g Gen) write_multi_return_types() { } name := typ.name.replace('.', '__') info := typ.info as table.MultiReturn - g.definitions.writeln('typedef struct {') + g.type_definitions.writeln('typedef struct {') // TODO copy pasta StructDecl // for field in struct_info.fields { for i, mr_typ in info.types { type_name := g.typ(mr_typ) - g.definitions.writeln('\t$type_name arg${i};') + g.type_definitions.writeln('\t$type_name arg${i};') } - g.definitions.writeln('} $name;\n') + g.type_definitions.writeln('} $name;\n') // g.typedefs.writeln('typedef struct $name $name;') } } pub fn (mut g Gen) write_variadic_types() { if g.variadic_args.size > 0 { - g.definitions.writeln('// variadic structs') + g.type_definitions.writeln('// variadic structs') } for type_str, arg_len in g.variadic_args { typ := table.Type(type_str.int()) type_name := g.typ(typ) struct_name := 'varg_' + type_name.replace('*', '_ptr') - g.definitions.writeln('struct $struct_name {') - g.definitions.writeln('\tint len;') - g.definitions.writeln('\t$type_name args[$arg_len];') - g.definitions.writeln('};\n') + g.type_definitions.writeln('struct $struct_name {') + g.type_definitions.writeln('\tint len;') + g.type_definitions.writeln('\t$type_name args[$arg_len];') + g.type_definitions.writeln('};\n') g.typedefs.writeln('typedef struct $struct_name $struct_name;') } } @@ -2223,26 +2261,26 @@ fn (g Gen) expr_is_multi_return_call(expr ast.Expr) bool { } fn (mut g Gen) return_statement(node ast.Return) { - g.write('return ') if g.fn_decl.name == 'main' { - g.writeln('0;') + g.writeln('return 0;') return } // got to do a correct check for multireturn sym := g.table.get_type_symbol(g.fn_decl.return_type) fn_return_is_multi := sym.kind == .multi_return fn_return_is_optional := g.fn_decl.return_type.flag_is(.optional) - // handle none/error for optional + // handle promoting none/error/function returning 'Option' if fn_return_is_optional { optional_none := node.exprs[0] is ast.None - mut optional_error := false - match node.exprs[0] { - ast.CallExpr { optional_error = it.name == 'error' } - else { false } - } - if optional_none || optional_error { + mut is_regular_option := g.typ(node.types[0]) == 'Option' + if optional_none || is_regular_option { + tmp := g.new_tmp_var() + g.write('/*opt promotion*/ Option $tmp = ') g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) g.write(';') + + styp := g.typ(g.fn_decl.return_type) + g.writeln('return *($styp*)&$tmp;') return } } @@ -2251,10 +2289,20 @@ fn (mut g Gen) return_statement(node ast.Return) { // typ_sym := g.table.get_type_symbol(g.fn_decl.return_type) // mr_info := typ_sym.info as table.MultiReturn mut styp := '' + + mut opt_tmp := '' + mut opt_type := '' + if fn_return_is_optional { + opt_type = g.typ(g.fn_decl.return_type) + // Create a tmp for this option + opt_tmp = g.new_tmp_var() + g.write('$opt_type $opt_tmp;') styp = g.base_type(g.fn_decl.return_type) - g.write('opt_ok(&($styp/*X*/[]) { ') + + g.write('opt_ok2(&($styp/*X*/[]) { ') } else { + g.write('return ') styp = g.typ(g.fn_decl.return_type) } // Use this to keep the tmp assignments in order @@ -2275,6 +2323,7 @@ fn (mut g Gen) return_statement(node ast.Return) { g.writeln(';') multi_unpack += g.go_before_stmt(0) g.write(s) + expr_types := expr_sym.mr_info().types for j, _ in expr_types { g.write('.arg$arg_idx=${tmp}.arg$j') @@ -2294,7 +2343,8 @@ fn (mut g Gen) return_statement(node ast.Return) { } g.write('}') if fn_return_is_optional { - g.write(' }, sizeof($styp))') + g.writeln(' }, (OptionBase*)(&$opt_tmp), sizeof($styp));') + g.write('return $opt_tmp') } // Make sure to add our unpacks g.insert_before_stmt(multi_unpack) @@ -2305,7 +2355,11 @@ fn (mut g Gen) return_statement(node ast.Return) { if fn_return_is_optional && !node.types[0].flag_is(.optional) && return_sym.name != 'Option' { styp := g.base_type(g.fn_decl.return_type) - g.write('/*$return_sym.name*/opt_ok(&($styp[]) { ') + opt_type := g.typ(g.fn_decl.return_type) + // Create a tmp for this option + opt_tmp := g.new_tmp_var() + g.write('$opt_type $opt_tmp;') + g.write('/*:)$return_sym.name*/opt_ok2(&($styp[]) { ') if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() { // Automatic Dereference for optional g.write('*') @@ -2316,9 +2370,11 @@ fn (mut g Gen) return_statement(node ast.Return) { g.write(', ') } } - g.writeln(' }, sizeof($styp));') + g.writeln(' }, (OptionBase*)(&$opt_tmp), sizeof($styp));') + g.writeln('return $opt_tmp;') return } + g.write('return ') cast_interface := sym.kind == .interface_ && node.types[0] != g.fn_decl.return_type if cast_interface { g.interface_call(node.types[0], g.fn_decl.return_type) @@ -2327,6 +2383,8 @@ fn (mut g Gen) return_statement(node ast.Return) { if cast_interface { g.write(')') } + } else { + g.write('return') } g.writeln(';') } @@ -2653,8 +2711,8 @@ fn (mut g Gen) write_sorted_types() { // sort structs types_sorted := g.sort_structs(types) // Generate C code - g.definitions.writeln('// builtin types:') - g.definitions.writeln('//------------------ #endbuiltin') + g.type_definitions.writeln('// builtin types:') + g.type_definitions.writeln('//------------------ #endbuiltin') g.write_types(types_sorted) } @@ -2668,39 +2726,59 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) { match typ.info { table.Struct { info := typ.info as table.Struct - // g.definitions.writeln('typedef struct {') + // TODO avoid buffer manip + start_pos := g.type_definitions.len if info.is_union { - g.definitions.writeln('union $name {') + g.type_definitions.writeln('union $name {') } else { - g.definitions.writeln('struct $name {') + g.type_definitions.writeln('struct $name {') } if info.fields.len > 0 { for field in info.fields { + // Some of these structs may want to contain + // optionals that may not be defined at this point + // if this is the case then we are going to + // buffer manip out in front of the struct + // write the optional in and then continue + if field.typ.flag_is(.optional) { + // Dont use g.typ() here becuase it will register + // optional and we dont want that + last_text := g.type_definitions.after(start_pos).clone() + g.type_definitions.go_back_to(start_pos) + styp, base := g.optional_type_name(field.typ) + g.optionals << styp + g.typedefs2.writeln('typedef struct $styp $styp;') + g.type_definitions.writeln('${g.optional_type_text(styp, base)};') + g.type_definitions.write(last_text) + } + type_name := g.typ(field.typ) + field_name := c_name(field.name) - g.definitions.writeln('\t$type_name $field_name;') + + g.type_definitions.writeln('\t$type_name $field_name;') } } else { - g.definitions.writeln('EMPTY_STRUCT_DECLARATION;') + g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;') } - // g.definitions.writeln('} $name;\n') + // g.type_definitions.writeln('} $name;\n') // - g.definitions.writeln('};\n') + g.type_definitions.writeln('};\n') } table.Alias { // table.Alias, table.SumType { TODO } table.SumType { - g.definitions.writeln('') - g.definitions.writeln('// Sum type $name = ') + g.type_definitions.writeln('') + g.type_definitions.writeln('// Sum type $name = ') for sv in it.variants { - g.definitions.writeln('// | ${sv:4d} = ${g.typ(sv):-20s}') + g.type_definitions.writeln('// | ${sv:4d} = ${g.typ(sv):-20s}') } - g.definitions.writeln('typedef struct {') - g.definitions.writeln(' void* obj;') - g.definitions.writeln(' int typ;') - g.definitions.writeln('} $name;') - g.definitions.writeln('') + g.type_definitions.writeln('typedef struct {') + g.type_definitions.writeln(' void* obj;') + g.type_definitions.writeln(' int typ;') + g.type_definitions.writeln('} $name;') + g.type_definitions.writeln('') } table.ArrayFixed { // .array_fixed { @@ -2712,7 +2790,7 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) { if fixed.starts_with('C__') { fixed = fixed[3..] } - g.definitions.writeln('typedef $fixed $styp [$len];') + g.type_definitions.writeln('typedef $fixed $styp [$len];') // } } else {} @@ -2987,8 +3065,8 @@ fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { } } } else if g.typ(node.expr_types[i]).starts_with('Option') { - str_fn_name := 'Option_str' - g.write('${str_fn_name}(*(Option*)&') + str_fn_name := 'OptionBase_str' + g.write('${str_fn_name}(*(OptionBase*)&') g.expr(expr) g.write(')') } else { @@ -3166,7 +3244,9 @@ fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type table. g.writeln('\tv_panic(${cvar_name}.v_error);') } } else { - g.writeln('\treturn $cvar_name;') + // Now that option types are distinct we need a cast here + styp := g.typ(g.fn_decl.return_type) + g.writeln('\treturn *($styp *)&$cvar_name;') } } g.write('}') @@ -3560,17 +3640,17 @@ fn (mut g Gen) go_stmt(node ast.GoStmt) { if name in g.threaded_fns { return } - g.definitions.writeln('\ntypedef struct $wrapper_struct_name {') + g.type_definitions.writeln('\ntypedef struct $wrapper_struct_name {') if it.is_method { styp := g.typ(it.receiver_type) - g.definitions.writeln('\t$styp arg0;') + g.type_definitions.writeln('\t$styp arg0;') } for i, arg in it.args { styp := g.typ(arg.typ) - g.definitions.writeln('\t$styp arg${i+1};') + g.type_definitions.writeln('\t$styp arg${i+1};') } - g.definitions.writeln('} $wrapper_struct_name;') - g.definitions.writeln('void* ${wrapper_fn_name}($wrapper_struct_name *arg);') + g.type_definitions.writeln('} $wrapper_struct_name;') + g.type_definitions.writeln('void* ${wrapper_fn_name}($wrapper_struct_name *arg);') g.gowrappers.writeln('void* ${wrapper_fn_name}($wrapper_struct_name *arg) {') g.gowrappers.write('\t${name}(') if it.is_method { @@ -3667,7 +3747,7 @@ fn (mut g Gen) gen_str_for_type_with_styp(typ table.Type, styp string) string { already_generated_key_no_ptr := '${styp}:${str_fn_name_no_ptr}' if already_generated_key_no_ptr !in g.str_types { g.str_types << already_generated_key_no_ptr - g.definitions.writeln('string ${str_fn_name_no_ptr}(${styp} it); // auto no_ptr version') + g.type_definitions.writeln('string ${str_fn_name_no_ptr}(${styp} it); // auto no_ptr version') g.auto_str_funcs.writeln('string ${str_fn_name_no_ptr}(${styp} it){ return ${str_fn_name}(&it); }') } /* @@ -3724,7 +3804,7 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) { } else { verror("could not generate string method for type \'${styp}\'") } - g.definitions.writeln('string ${str_fn_name}($styp it); // auto') + g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) {') if convertor == 'bool' { g.auto_str_funcs.writeln('\tstring tmp1 = string_add(tos_lit("${styp}("), (${convertor})it ? tos_lit("true") : tos_lit("false"));') @@ -3739,7 +3819,7 @@ fn (mut g Gen) gen_str_default(sym table.TypeSymbol, styp, str_fn_name string) { fn (mut g Gen) gen_str_for_enum(info table.Enum, styp, str_fn_name string) { s := styp.replace('.', '__') - g.definitions.writeln('string ${str_fn_name}($styp it); // auto') + g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { /* gen_str_for_enum */') g.auto_str_funcs.writeln('\tswitch(it) {') for val in info.vals { @@ -3764,7 +3844,7 @@ fn (mut g Gen) gen_str_for_struct(info table.Struct, styp, str_fn_name string) { fnames2strfunc[field_styp] = field_fn_name } } - g.definitions.writeln('string ${str_fn_name}($styp x, int indent_count); // auto') + g.type_definitions.writeln('string ${str_fn_name}($styp x, int indent_count); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp x, int indent_count) {') mut clean_struct_v_type_name := styp.replace('__', '.') if styp.ends_with('*') { @@ -3841,7 +3921,7 @@ fn (mut g Gen) gen_str_for_array(info table.Array, styp, str_fn_name string) { // eprintln('> sym.name: does not have method `str`') g.gen_str_for_type_with_styp(info.elem_type, field_styp) } - g.definitions.writeln('string ${str_fn_name}($styp a); // auto') + g.type_definitions.writeln('string ${str_fn_name}($styp a); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp a) {') g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(a.len * 10);') g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos_lit("["));') @@ -3902,7 +3982,7 @@ fn (mut g Gen) gen_str_for_array_fixed(info table.ArrayFixed, styp, str_fn_name if !sym.has_method('str') { g.gen_str_for_type_with_styp(info.elem_type, field_styp) } - g.definitions.writeln('string ${str_fn_name}($styp a); // auto') + g.type_definitions.writeln('string ${str_fn_name}($styp a); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp a) {') g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder($info.size * 10);') g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos_lit("["));') @@ -3944,7 +4024,7 @@ fn (mut g Gen) gen_str_for_map(info table.Map, styp, str_fn_name string) { g.gen_str_for_type_with_styp(info.value_type, val_styp) } zero := g.type_default(info.value_type) - g.definitions.writeln('string ${str_fn_name}($styp m); // auto') + g.type_definitions.writeln('string ${str_fn_name}($styp m); // auto') g.auto_str_funcs.writeln('string ${str_fn_name}($styp m) { /* gen_str_for_map */') g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(m.key_values.size*10);') g.auto_str_funcs.writeln('\tstrings__Builder_write(&sb, tos_lit("{"));') diff --git a/vlib/v/gen/json.v b/vlib/v/gen/json.v index 20764d0338..522b27d6d4 100644 --- a/vlib/v/gen/json.v +++ b/vlib/v/gen/json.v @@ -37,16 +37,20 @@ fn (mut g Gen) gen_json_for_type(typ table.Type) { // cJSON_Parse(str) call is added by the compiler // Code gen decoder dec_fn_name := js_dec_name(sym.name) + + // Make sure that this optional type actually exists + g.register_optional(typ) dec.writeln(' -//Option ${dec_fn_name}(cJSON* root, $styp* res) { -Option ${dec_fn_name}(cJSON* root) { +//Option_$styp ${dec_fn_name}(cJSON* root, $styp* res) { +Option_$styp ${dec_fn_name}(cJSON* root) { $styp res; if (!root) { const char *error_ptr = cJSON_GetErrorPtr(); if (error_ptr != NULL) { // fprintf(stderr, "Error in decode() for $styp error_ptr=: %%s\\n", error_ptr); // printf("\\nbad js=%%s\\n", js.str); - return v_error(tos2(error_ptr)); + Option err = v_error(tos2(error_ptr)); + return *(Option_$styp *)&err; } } ') @@ -101,7 +105,9 @@ cJSON* ${enc_fn_name}($styp val) { } // cJSON_delete // p.cgen.fns << '$dec return opt_ok(res); \n}' - dec.writeln('return opt_ok(&res, sizeof(res)); \n}') + dec.writeln('Option_$styp ret;') + dec.writeln('opt_ok2(&res, (OptionBase*)&ret, sizeof(res));') + dec.writeln('return ret;\n}') enc.writeln('\treturn o;\n}') g.definitions.writeln(dec.str()) g.gowrappers.writeln(enc.str()) diff --git a/vlib/v/tests/option_test.v b/vlib/v/tests/option_test.v index 8b0619a6e5..a32e71a948 100644 --- a/vlib/v/tests/option_test.v +++ b/vlib/v/tests/option_test.v @@ -213,6 +213,13 @@ mut: opt ?int } +struct Thing2 { +mut: + opt ?Thing +} + + + fn test_opt_field() { /* QTODO