diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index e4fb78c1b7..a62e7949a9 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -884,21 +884,22 @@ pub fn (t &Table) known_type_idx(typ Type) bool { pub fn (t &Table) array_name(elem_type Type) string { elem_type_sym := t.sym(elem_type) ptr := if elem_type.is_ptr() { '&'.repeat(elem_type.nr_muls()) } else { '' } - return '[]$ptr$elem_type_sym.name' + opt := if elem_type.has_flag(.optional) { '?' } else { '' } + res := if elem_type.has_flag(.result) { '!' } else { '' } + return '[]$opt$res$ptr$elem_type_sym.name' } [inline] pub fn (t &Table) array_cname(elem_type Type) string { elem_type_sym := t.sym(elem_type) - mut res := '' - if elem_type.is_ptr() { - res = '_ptr'.repeat(elem_type.nr_muls()) - } + suffix := if elem_type.is_ptr() { '_ptr'.repeat(elem_type.nr_muls()) } else { '' } + opt := if elem_type.has_flag(.optional) { '_option_' } else { '' } + res := if elem_type.has_flag(.result) { '_result_' } else { '' } if elem_type_sym.cname.contains('<') { type_name := elem_type_sym.cname.replace_each(['<', '_T_', ', ', '_', '>', '']) - return 'Array_$type_name' + res + return 'Array_$opt$res$type_name$suffix' } else { - return 'Array_$elem_type_sym.cname' + res + return 'Array_$opt$res$elem_type_sym.cname$suffix' } } @@ -908,22 +909,28 @@ pub fn (t &Table) array_cname(elem_type Type) string { pub fn (t &Table) array_fixed_name(elem_type Type, size int, size_expr Expr) string { elem_type_sym := t.sym(elem_type) ptr := if elem_type.is_ptr() { '&'.repeat(elem_type.nr_muls()) } else { '' } + opt := if elem_type.has_flag(.optional) { '?' } else { '' } + res := if elem_type.has_flag(.result) { '!' } else { '' } size_str := if size_expr is EmptyExpr || size != 987654321 { size.str() } else { size_expr.str() } - return '[$size_str]$ptr$elem_type_sym.name' + return '[$size_str]$opt$res$ptr$elem_type_sym.name' } [inline] pub fn (t &Table) array_fixed_cname(elem_type Type, size int) string { elem_type_sym := t.sym(elem_type) - mut res := '' - if elem_type.is_ptr() { - res = '_ptr$elem_type.nr_muls()' + suffix := if elem_type.is_ptr() { '_ptr$elem_type.nr_muls()' } else { '' } + opt := if elem_type.has_flag(.optional) { '_option_' } else { '' } + res := if elem_type.has_flag(.result) { '_result_' } else { '' } + if elem_type_sym.cname.contains('<') { + type_name := elem_type_sym.cname.replace_each(['<', '_T_', ', ', '_', '>', '']) + return 'Array_fixed_$opt$res$type_name${suffix}_$size' + } else { + return 'Array_fixed_$opt$res$elem_type_sym.cname${suffix}_$size' } - return 'Array_fixed_$elem_type_sym.cname${res}_$size' } [inline] @@ -1012,7 +1019,7 @@ pub fn (t &Table) thread_cname(return_type Type) string { pub fn (t &Table) map_name(key_type Type, value_type Type) string { key_type_sym := t.sym(key_type) value_type_sym := t.sym(value_type) - ptr := if value_type.is_ptr() { '&' } else { '' } + ptr := if value_type.is_ptr() { '&'.repeat(value_type.nr_muls()) } else { '' } opt := if value_type.has_flag(.optional) { '?' } else { '' } res := if value_type.has_flag(.result) { '!' } else { '' } return 'map[$key_type_sym.name]$opt$res$ptr$value_type_sym.name' @@ -1022,10 +1029,15 @@ pub fn (t &Table) map_name(key_type Type, value_type Type) string { pub fn (t &Table) map_cname(key_type Type, value_type Type) string { key_type_sym := t.sym(key_type) value_type_sym := t.sym(value_type) - suffix := if value_type.is_ptr() { '_ptr' } else { '' } + suffix := if value_type.is_ptr() { '_ptr'.repeat(value_type.nr_muls()) } else { '' } opt := if value_type.has_flag(.optional) { '_option_' } else { '' } res := if value_type.has_flag(.result) { '_result_' } else { '' } - return 'Map_${key_type_sym.cname}_$opt$res$value_type_sym.cname$suffix' + if value_type_sym.cname.contains('<') { + type_name := value_type_sym.cname.replace_each(['<', '_T_', ', ', '_', '>', '']) + return 'Map_${key_type_sym.cname}_$opt$res$type_name$suffix' + } else { + return 'Map_${key_type_sym.cname}_$opt$res$value_type_sym.cname$suffix' + } } pub fn (mut t Table) find_or_register_chan(elem_type Type, is_mut bool) int { diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index bf0264ac4c..4f27803a00 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -72,7 +72,9 @@ mut: embedded_data strings.Builder // data to embed in the executable/binary shared_types strings.Builder // shared/lock types shared_functions strings.Builder // shared constructors - options strings.Builder // `option_xxxx` types + out_options_forward strings.Builder // forward `option_xxxx` types + out_options strings.Builder // `option_xxxx` types + out_results_forward strings.Builder // forward`result_xxxx` types out_results strings.Builder // `result_xxxx` types json_forward_decls strings.Builder // json type forward decls sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc @@ -99,9 +101,12 @@ mut: is_json_fn bool // inside json.encode() is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)` is_fn_index_call bool - is_cc_msvc bool // g.pref.ccompiler == 'msvc' - vlines_path string // set to the proper path for generating #line directives + is_cc_msvc bool // g.pref.ccompiler == 'msvc' + vlines_path string // set to the proper path for generating #line directives + optionals_pos_forward int // insertion point to forward + options_forward []string // to forward optionals map[string]string // to avoid duplicates + results_forward []string // to forward results map[string]string // to avoid duplicates done_optionals shared []string // to avoid duplicates chan_pop_optionals map[string]string // types for `x := <-ch or {...}` @@ -270,7 +275,9 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string, dump_funcs: strings.new_builder(100) pcs_declarations: strings.new_builder(100) embedded_data: strings.new_builder(1000) - options: strings.new_builder(100) + out_options_forward: strings.new_builder(100) + out_options: strings.new_builder(100) + out_results_forward: strings.new_builder(100) out_results: strings.new_builder(100) shared_types: strings.new_builder(100) shared_functions: strings.new_builder(100) @@ -456,6 +463,22 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string, g.write_init_function() } + // insert for optionals forward + if g.out_options_forward.len > 0 || g.out_results_forward.len > 0 { + tail := g.type_definitions.cut_to(g.optionals_pos_forward) + if g.out_options_forward.len > 0 { + g.type_definitions.writeln('// #start V forward option_xxx definitions:') + g.type_definitions.writeln(g.out_options_forward.str()) + g.type_definitions.writeln('// #end V forward option_xxx definitions\n') + } + if g.out_results_forward.len > 0 { + g.type_definitions.writeln('// #start V forward result_xxx definitions:') + g.type_definitions.writeln(g.out_results_forward.str()) + g.type_definitions.writeln('// #end V forward result_xxx definitions\n') + } + g.type_definitions.writeln(tail) + } + g.finish() mut b := strings.new_builder(640000) @@ -488,7 +511,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string, b.writeln('\n// V shared types:') b.write_string(g.shared_types.str()) b.writeln('\n// V Option_xxx definitions:') - b.write_string(g.options.str()) + b.write_string(g.out_options.str()) b.writeln('\n// V result_xxx definitions:') b.write_string(g.out_results.str()) b.writeln('\n// V json forward decls:') @@ -591,7 +614,9 @@ fn cgen_process_one_file_cb(p &pool.PoolProcessor, idx int, wid int) &Gen { pcs_declarations: strings.new_builder(100) hotcode_definitions: strings.new_builder(100) embedded_data: strings.new_builder(1000) - options: strings.new_builder(100) + out_options_forward: strings.new_builder(100) + out_options: strings.new_builder(100) + out_results_forward: strings.new_builder(100) out_results: strings.new_builder(100) shared_types: strings.new_builder(100) shared_functions: strings.new_builder(100) @@ -615,6 +640,8 @@ fn cgen_process_one_file_cb(p &pool.PoolProcessor, idx int, wid int) &Gen { array_sort_fn: global_g.array_sort_fn waiter_fns: global_g.waiter_fns threaded_fns: global_g.threaded_fns + options_forward: global_g.options_forward + results_forward: global_g.results_forward done_optionals: global_g.done_optionals is_autofree: global_g.pref.autofree referenced_fns: global_g.referenced_fns @@ -650,7 +677,9 @@ pub fn (mut g Gen) free_builders() { g.shared_functions.free() g.channel_definitions.free() g.thread_definitions.free() - g.options.free() + g.out_options_forward.free() + g.out_options.free() + g.out_results_forward.free() g.out_results.free() g.json_forward_decls.free() g.enum_typedefs.free() @@ -734,6 +763,7 @@ pub fn (mut g Gen) init() { g.cheaders.writeln('#include ') } g.write_builtin_types() + g.optionals_pos_forward = g.type_definitions.len g.write_typedef_types() g.write_typeof_functions() g.write_sorted_types() @@ -1086,7 +1116,11 @@ fn (mut g Gen) write_optionals() { } done << base g.typedefs.writeln('typedef struct $styp $styp;') - g.options.write_string(g.optional_type_text(styp, base) + ';\n\n') + if base in g.options_forward { + g.out_options_forward.write_string(g.optional_type_text(styp, base) + ';\n\n') + } else { + g.out_options.write_string(g.optional_type_text(styp, base) + ';\n\n') + } } } @@ -1098,7 +1132,11 @@ fn (mut g Gen) write_results() { } done << base g.typedefs.writeln('typedef struct $styp $styp;') - g.out_results.write_string(g.result_type_text(styp, base) + ';\n\n') + if base in g.results_forward { + g.out_results_forward.write_string(g.result_type_text(styp, base) + ';\n\n') + } else { + g.out_results.write_string(g.result_type_text(styp, base) + ';\n\n') + } } for k, _ in g.table.anon_struct_names { ck := c_name(k) @@ -1314,6 +1352,12 @@ pub fn (mut g Gen) write_typedef_types() { g.type_definitions.writeln(def_str) } else { g.type_definitions.writeln('typedef $fixed $styp [$len];') + base := g.typ(info.elem_type.clear_flag(.optional).clear_flag(.result)) + if info.elem_type.has_flag(.optional) && base !in g.options_forward { + g.options_forward << base + } else if info.elem_type.has_flag(.result) && base !in g.results_forward { + g.results_forward << base + } } } } @@ -2272,7 +2316,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ return } if got_sym.info !is ast.Interface && exp_sym.info is ast.Interface - && got_type.idx() != expected_type.idx() && !expected_type.has_flag(.optional) { + && got_type.idx() != expected_type.idx() && !expected_type.has_flag(.optional) + && !expected_type.has_flag(.result) { if expr is ast.StructInit && !got_type.is_ptr() { g.inside_cast_in_heap++ got_styp := g.cc_type(got_type.ref(), true) @@ -2362,7 +2407,7 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ // Generic dereferencing logic neither_void := ast.voidptr_type !in [got_type, expected_type] if expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f) - && !expected_type.has_flag(.optional) { + && !expected_type.has_flag(.optional) && !expected_type.has_flag(.result) { shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*` if got_type_raw.is_ptr() { g.error('cannot convert reference to `shared`', expr.pos()) @@ -2393,8 +2438,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ got_deref_type := got_type.deref() deref_sym := g.table.sym(got_deref_type) deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx] - got_is_opt := got_type.has_flag(.optional) - if deref_will_match || got_is_opt || expr.is_auto_deref_var() { + got_is_opt_or_res := got_type.has_flag(.optional) || got_type.has_flag(.result) + if deref_will_match || got_is_opt_or_res || expr.is_auto_deref_var() { g.write('*') } } @@ -3452,8 +3497,9 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } sym := g.table.sym(g.unwrap_generic(node.expr_type)) // if node expr is a root ident and an optional - mut is_optional := node.expr is ast.Ident && node.expr_type.has_flag(.optional) - if is_optional { + mut is_opt_or_res := node.expr is ast.Ident + && (node.expr_type.has_flag(.optional) || node.expr_type.has_flag(.result)) + if is_opt_or_res { opt_base_typ := g.base_type(node.expr_type) g.writeln('(*($opt_base_typ*)') } @@ -3584,7 +3630,7 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } else { g.expr(node.expr) } - if is_optional { + if is_opt_or_res { g.write('.data)') } // struct embedding diff --git a/vlib/v/tests/array_elements_with_optional_test.v b/vlib/v/tests/array_elements_with_optional_test.v new file mode 100644 index 0000000000..1e3d3e0f96 --- /dev/null +++ b/vlib/v/tests/array_elements_with_optional_test.v @@ -0,0 +1,44 @@ +struct Foo1 { + arr1 [5]?int + arr2 [5]int +} + +fn get_has_optional_fixed() ?int { + foo := Foo1{} + return foo.arr1[0] +} + +fn get_no_optional_fixed() int { + foo := Foo1{} + return foo.arr2[0] +} + +fn test_optional_fixed() ? { + x := get_has_optional_fixed()? + assert x == 0 + assert get_no_optional_fixed() == 0 +} + +struct Foo2 { +mut: + arr1 []?int + arr2 []int +} + +fn get_has_optional() ?int { + mut foo := Foo2{} + foo.arr1 << 0 + return foo.arr1[0] +} + +fn get_no_optional() int { + mut foo := Foo2{} + foo.arr2 << 0 + return foo.arr2[0] +} + +fn test_optional_non_fixed() ? { + x := get_has_optional()? + assert x == 0 + assert get_no_optional() == 0 +}