From 9d9785cc053096fe515eedf83242f4e8546e281c Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Thu, 18 May 2023 06:30:49 -0300 Subject: [PATCH] all: allow fixed array returns (#17931) --- vlib/v/ast/table.v | 15 ++-- vlib/v/ast/types.v | 1 + vlib/v/checker/containers.v | 6 +- vlib/v/checker/fn.v | 7 +- vlib/v/checker/return.v | 5 ++ .../tests/return_aliases_of_fixed_array.out | 4 +- vlib/v/checker/tests/return_fixed_array.out | 19 ++-- vlib/v/checker/tests/return_fixed_array.vv | 6 ++ vlib/v/gen/c/assign.v | 50 +++++++---- vlib/v/gen/c/auto_str_methods.v | 20 +++-- vlib/v/gen/c/cgen.v | 52 ++++++++++- vlib/v/gen/c/fn.v | 4 + vlib/v/gen/c/index.v | 4 + vlib/v/parser/comptime.v | 2 +- vlib/v/parser/parse_type.v | 11 +-- vlib/v/tests/fn_fixed_array_ret_test.v | 90 +++++++++++++++++++ 16 files changed, 236 insertions(+), 60 deletions(-) create mode 100644 vlib/v/tests/fn_fixed_array_ret_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index b670defedd..3ef1bee64b 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1061,14 +1061,15 @@ pub fn (mut t Table) find_or_register_array_with_dims(elem_type Type, nr_dims in return t.find_or_register_array(t.find_or_register_array_with_dims(elem_type, nr_dims - 1)) } -pub fn (mut t Table) find_or_register_array_fixed(elem_type Type, size int, size_expr Expr) int { - name := t.array_fixed_name(elem_type, size, size_expr) +pub fn (mut t Table) find_or_register_array_fixed(elem_type Type, size int, size_expr Expr, is_fn_ret bool) int { + prefix := if is_fn_ret { '_v_' } else { '' } + name := prefix + t.array_fixed_name(elem_type, size, size_expr) // existing existing_idx := t.type_idxs[name] if existing_idx > 0 { return existing_idx } - cname := t.array_fixed_cname(elem_type, size) + cname := prefix + t.array_fixed_cname(elem_type, size) // register array_fixed_type := TypeSymbol{ kind: .array_fixed @@ -1078,6 +1079,7 @@ pub fn (mut t Table) find_or_register_array_fixed(elem_type Type, size int, size elem_type: elem_type size: size size_expr: size_expr + is_fn_ret: is_fn_ret } } return t.register_sym(array_fixed_type) @@ -1381,7 +1383,8 @@ pub fn (mut t Table) bitsize_to_type(bit_size int) Type { if bit_size % 8 != 0 { // there is no way to do `i2131(32)` so this should never be reached t.panic('compiler bug: bitsizes must be multiples of 8') } - return new_type(t.find_or_register_array_fixed(u8_type, bit_size / 8, empty_expr)) + return new_type(t.find_or_register_array_fixed(u8_type, bit_size / 8, empty_expr, + false)) } } } @@ -1511,7 +1514,7 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names, concrete_types) { - idx := t.find_or_register_array_fixed(typ, sym.info.size, None{}) + idx := t.find_or_register_array_fixed(typ, sym.info.size, None{}, false) if typ.has_flag(.generic) { return new_type(idx).derive_add_muls(generic_type).set_flag(.generic) } else { @@ -1779,7 +1782,7 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr } ArrayFixed { unwrap_typ := t.unwrap_generic_type(ts.info.elem_type, generic_names, concrete_types) - idx := t.find_or_register_array_fixed(unwrap_typ, ts.info.size, None{}) + idx := t.find_or_register_array_fixed(unwrap_typ, ts.info.size, None{}, false) return new_type(idx).derive_add_muls(typ).clear_flag(.generic) } Chan { diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index 19cb4683e2..2f66e6331b 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -226,6 +226,7 @@ pub: size_expr Expr // used by fmt for e.g. ´[my_const]u8´ pub mut: elem_type Type + is_fn_ret bool } pub struct Chan { diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v index 5c83b0bfe9..7736997620 100644 --- a/vlib/v/checker/containers.v +++ b/vlib/v/checker/containers.v @@ -233,7 +233,8 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { } } if node.is_fixed { - idx := c.table.find_or_register_array_fixed(elem_type, node.exprs.len, ast.empty_expr) + idx := c.table.find_or_register_array_fixed(elem_type, node.exprs.len, ast.empty_expr, + false) if elem_type.has_flag(.generic) { node.typ = ast.new_type(idx).set_flag(.generic) } else { @@ -281,7 +282,8 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { c.error('fixed size cannot be zero or negative (fixed_size: ${fixed_size})', init_expr.pos()) } - idx := c.table.find_or_register_array_fixed(node.elem_type, int(fixed_size), init_expr) + idx := c.table.find_or_register_array_fixed(node.elem_type, int(fixed_size), init_expr, + false) if node.elem_type.has_flag(.generic) { node.typ = ast.new_type(idx).set_flag(.generic) } else { diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 36cd582994..b0f00cc53c 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -120,19 +120,16 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { return_sym := c.table.final_sym(node.return_type) if return_sym.info is ast.MultiReturn { for multi_type in return_sym.info.types { - multi_sym := c.table.sym(multi_type) if multi_type == ast.error_type { c.error('type `IError` cannot be used in multi-return, return an Option instead', node.return_type_pos) } else if multi_type.has_flag(.result) { c.error('result cannot be used in multi-return, return a Result instead', node.return_type_pos) - } else if multi_sym.kind == .array_fixed { - c.error('fixed array cannot be used in multi-return', node.return_type_pos) } } - } else if return_sym.kind == .array_fixed { - c.error('fixed array cannot be returned by function', node.return_type_pos) + } else if c.table.sym(node.return_type).kind == .alias && return_sym.kind == .array_fixed { + c.error('fixed array cannot be returned by function using alias', node.return_type_pos) } // Ensure each generic type of the parameter was declared in the function's definition if node.return_type.has_flag(.generic) { diff --git a/vlib/v/checker/return.v b/vlib/v/checker/return.v index ce2f1c174c..58014b2494 100644 --- a/vlib/v/checker/return.v +++ b/vlib/v/checker/return.v @@ -202,6 +202,11 @@ fn (mut c Checker) return_stmt(mut node ast.Return) { } continue } + if exp_type_sym.kind == .array_fixed && got_type_sym.kind == .array_fixed { + if (exp_type_sym.info as ast.ArrayFixed).size == (got_type_sym.info as ast.ArrayFixed).size && (exp_type_sym.info as ast.ArrayFixed).elem_type == (got_type_sym.info as ast.ArrayFixed).elem_type { + continue + } + } // `fn foo() !int { return Err{} }` if got_type_sym.kind == .struct_ && c.type_implements(got_type, ast.error_type, node.pos) { diff --git a/vlib/v/checker/tests/return_aliases_of_fixed_array.out b/vlib/v/checker/tests/return_aliases_of_fixed_array.out index f8ac3c16b0..3da5c4b732 100644 --- a/vlib/v/checker/tests/return_aliases_of_fixed_array.out +++ b/vlib/v/checker/tests/return_aliases_of_fixed_array.out @@ -1,11 +1,11 @@ -vlib/v/checker/tests/return_aliases_of_fixed_array.vv:8:18: error: fixed array cannot be returned by function +vlib/v/checker/tests/return_aliases_of_fixed_array.vv:8:18: error: fixed array cannot be returned by function using alias 6 | } 7 | 8 | fn (v Mat) foo() Mat { | ~~~ 9 | return v 10 | } -vlib/v/checker/tests/return_aliases_of_fixed_array.vv:12:10: error: fixed array cannot be returned by function +vlib/v/checker/tests/return_aliases_of_fixed_array.vv:12:10: error: fixed array cannot be returned by function using alias 10 | } 11 | 12 | fn bar() Mat { diff --git a/vlib/v/checker/tests/return_fixed_array.out b/vlib/v/checker/tests/return_fixed_array.out index 3ac7df0fe6..508868f028 100644 --- a/vlib/v/checker/tests/return_fixed_array.out +++ b/vlib/v/checker/tests/return_fixed_array.out @@ -1,12 +1,7 @@ -vlib/v/checker/tests/return_fixed_array.vv:1:25: error: fixed array cannot be returned by function - 1 | fn return_fixed_array() [3]int { - | ~~~~~~ - 2 | return [1, 2, 3]! - 3 | } -vlib/v/checker/tests/return_fixed_array.vv:5:41: error: fixed array cannot be used in multi-return - 3 | } - 4 | - 5 | fn return_fixed_array_in_multi_return() ([3]int, [3]int) { - | ~~~~~~~~~~~~~~~~ - 6 | return [1, 2, 3]!, [4, 5, 6]! - 7 | } +vlib/v/checker/tests/return_fixed_array.vv:11:25: error: fixed array cannot be returned by function using alias + 9 | } + 10 | + 11 | fn return_fixed_array() Abc { + | ~~~ + 12 | return [1, 2, 3]! + 13 | } diff --git a/vlib/v/checker/tests/return_fixed_array.vv b/vlib/v/checker/tests/return_fixed_array.vv index dc24da155c..66ec010d24 100644 --- a/vlib/v/checker/tests/return_fixed_array.vv +++ b/vlib/v/checker/tests/return_fixed_array.vv @@ -1,3 +1,5 @@ +type Abc = [3]int + fn return_fixed_array() [3]int { return [1, 2, 3]! } @@ -5,3 +7,7 @@ fn return_fixed_array() [3]int { fn return_fixed_array_in_multi_return() ([3]int, [3]int) { return [1, 2, 3]!, [4, 5, 6]! } + +fn return_fixed_array() Abc { + return [1, 2, 3]! +} \ No newline at end of file diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index a28d50e03f..04cbe44084 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -553,7 +553,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { } } if !is_used_var_styp { - g.write('${styp} ') + if !val_type.has_flag(.option) && left_sym.info is ast.ArrayFixed + && (left_sym.info as ast.ArrayFixed).is_fn_ret { + g.write('${styp[3..]} ') + } else { + g.write('${styp} ') + } } if is_auto_heap { g.write('*') @@ -613,6 +618,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { } else if is_fixed_array_var { // TODO Instead of the translated check, check if it's a pointer already // and don't generate memcpy & + right_is_fixed_ret := !(val is ast.CallExpr + && (val as ast.CallExpr).or_block.kind == .propagate_option) + && ((right_sym.info is ast.ArrayFixed + && (right_sym.info as ast.ArrayFixed).is_fn_ret) + || (val is ast.CallExpr + && g.table.sym(g.unwrap_generic((val as ast.CallExpr).return_type)).kind == .array_fixed)) typ_str := g.typ(val_type).trim('*') final_typ_str := if is_fixed_array_var { '' } else { '(${typ_str}*)' } final_ref_str := if is_fixed_array_var { @@ -631,7 +642,10 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { g.expr(left) g.write(', ${final_ref_str}') g.expr(val) - g.write(', sizeof(${typ_str}))') + if right_is_fixed_ret { + g.write('.ret_arr') + } + g.write(', sizeof(${typ_str})) /*assign*/') } } else if is_decl { g.is_shared = var_type.has_flag(.shared_f) @@ -755,21 +769,27 @@ fn (mut g Gen) gen_multi_return_assign(node &ast.AssignStmt, return_type ast.Typ } } else { g.expr(lx) - if g.is_arraymap_set { - if is_auto_heap { - g.writeln('HEAP${noscan}(${styp}, ${mr_var_name}.arg${i}) });') - } else if is_option { - g.writeln('(*((${g.base_type(return_type)}*)${mr_var_name}.data)).arg${i} });') - } else { - g.writeln('${mr_var_name}.arg${i} });') - } + sym := g.table.sym(node.left_types[i]) + if sym.kind == .array_fixed { + g.writeln(';') + g.writeln('memcpy(&${g.expr_string(lx)}, &${mr_var_name}.arg${i}, sizeof(${styp}));') } else { - if is_auto_heap { - g.writeln(' = HEAP${noscan}(${styp}, ${mr_var_name}.arg${i});') - } else if is_option { - g.writeln(' = (*((${g.base_type(return_type)}*)${mr_var_name}.data)).arg${i};') + if g.is_arraymap_set { + if is_auto_heap { + g.writeln('HEAP${noscan}(${styp}, ${mr_var_name}.arg${i}) });') + } else if is_option { + g.writeln('(*((${g.base_type(return_type)}*)${mr_var_name}.data)).arg${i} });') + } else { + g.writeln('${mr_var_name}.arg${i} });') + } } else { - g.writeln(' = ${mr_var_name}.arg${i};') + if is_auto_heap { + g.writeln(' = HEAP${noscan}(${styp}, ${mr_var_name}.arg${i});') + } else if is_option { + g.writeln(' = (*((${g.base_type(return_type)}*)${mr_var_name}.data)).arg${i};') + } else { + g.writeln(' = ${mr_var_name}.arg${i};') + } } } } diff --git a/vlib/v/gen/c/auto_str_methods.v b/vlib/v/gen/c/auto_str_methods.v index 4e6e8cb54e..6646594e57 100644 --- a/vlib/v/gen/c/auto_str_methods.v +++ b/vlib/v/gen/c/auto_str_methods.v @@ -657,6 +657,7 @@ fn (mut g Gen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_fn_ g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(${info.size} * 10);') g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("["));') g.auto_str_funcs.writeln('\tfor (int i = 0; i < ${info.size}; ++i) {') + suffix := if info.is_fn_ret { '.ret_arr' } else { '' } if sym.kind == .function { g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();') g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, x);') @@ -665,27 +666,30 @@ fn (mut g Gen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_fn_ if should_use_indent_func(sym.kind) && !sym_has_str_method { if is_elem_ptr { g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("${deref_label}"));') - g.auto_str_funcs.writeln('\t\tif ( 0 == a[i] ) {') + g.auto_str_funcs.writeln('\t\tif ( 0 == a${suffix}[i] ) {') g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT("0"));') g.auto_str_funcs.writeln('\t\t}else{') - g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( ${deref} a[i]) );') + g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( ${deref} a${suffix}[i]) );') g.auto_str_funcs.writeln('\t\t}') } else { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( ${deref} a[i]) );') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( ${deref} a${suffix}[i]) );') } } else if sym.kind in [.f32, .f64] { if sym.kind == .f32 { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g32('a[i]')} );') + field_str := str_intp_g32('a${suffix}[i]') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${field_str} );') } else { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g64('a[i]')} );') + field_str := str_intp_g64('a${suffix}[i]') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${field_str} );') } } else if sym.kind == .string { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_sq('a[i]')});') + field_str := str_intp_sq('a${suffix}[i]') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${field_str});') } else if sym.kind == .rune { - tmp_str := str_intp_rune('${elem_str_fn_name}( ${deref} a[i])') + tmp_str := str_intp_rune('${elem_str_fn_name}( ${deref} a${suffix}[i])') g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${tmp_str});') } else { - g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( ${deref} a[i]));') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( ${deref} a${suffix}[i]));') } } g.auto_str_funcs.writeln('\t\tif (i < ${info.size - 1}) {') diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index fd958d8007..53348ac37e 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -799,6 +799,7 @@ pub fn (mut g Gen) init() { g.write_typedef_types() g.write_typeof_functions() g.write_sorted_types() + g.write_array_fixed_return_types() g.write_multi_return_types() g.definitions.writeln('// end of definitions #endif') if g.pref.compile_defines_all.len > 0 { @@ -1390,7 +1391,7 @@ pub fn (mut g Gen) write_typedef_types() { mut def_str := 'typedef ${fixed};' def_str = def_str.replace_once('(*)', '(*${styp}[${len}])') g.type_definitions.writeln(def_str) - } else { + } else if !info.is_fn_ret { g.type_definitions.writeln('typedef ${fixed} ${styp} [${len}];') base := g.typ(info.elem_type.clear_flags(.option, .result)) if info.elem_type.has_flag(.option) && base !in g.options_forward { @@ -1573,6 +1574,30 @@ pub fn (mut g Gen) write_fn_typesymbol_declaration(sym ast.TypeSymbol) { } } +pub fn (mut g Gen) write_array_fixed_return_types() { + g.typedefs.writeln('\n// BEGIN_array_fixed_return_typedefs') + g.type_definitions.writeln('\n// BEGIN_array_fixed_return_structs') + + for sym in g.table.type_symbols { + if sym.kind != .array_fixed || (sym.info as ast.ArrayFixed).elem_type.has_flag(.generic) + || !(sym.info as ast.ArrayFixed).is_fn_ret { + continue + } + info := sym.info as ast.ArrayFixed + mut fixed_elem_name := g.typ(info.elem_type.set_nr_muls(0)) + if info.elem_type.is_ptr() { + fixed_elem_name += '*'.repeat(info.elem_type.nr_muls()) + } + g.typedefs.writeln('typedef struct ${sym.cname} ${sym.cname};') + g.type_definitions.writeln('struct ${sym.cname} {') + g.type_definitions.writeln('\t${fixed_elem_name} ret_arr[${info.size}];') + g.type_definitions.writeln('};') + } + + g.typedefs.writeln('// END_array_fixed_return_typedefs\n') + g.type_definitions.writeln('// END_array_fixed_return_structs\n') +} + pub fn (mut g Gen) write_multi_return_types() { start_pos := g.type_definitions.len g.typedefs.writeln('\n// BEGIN_multi_return_typedefs') @@ -4564,6 +4589,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { fn_return_is_multi := sym.kind == .multi_return fn_return_is_option := fn_ret_type.has_flag(.option) fn_return_is_result := fn_ret_type.has_flag(.result) + fn_return_is_fixed_array := sym.kind == .array_fixed && !fn_ret_type.has_flag(.option) mut has_semicolon := false if node.exprs.len == 0 { @@ -4581,10 +4607,14 @@ fn (mut g Gen) return_stmt(node ast.Return) { return } tmpvar := g.new_tmp_var() - ret_typ := g.typ(g.unwrap_generic(fn_ret_type)) + mut ret_typ := g.typ(g.unwrap_generic(fn_ret_type)) + if fn_ret_type.has_flag(.generic) && fn_return_is_fixed_array { + ret_typ = '_v_${ret_typ}' + } mut use_tmp_var := g.defer_stmts.len > 0 || g.defer_profile_code.len > 0 || g.cur_lock.lockeds.len > 0 || (fn_return_is_multi && node.exprs.len >= 1 && fn_return_is_option) + || fn_return_is_fixed_array // handle promoting none/error/function returning _option' if fn_return_is_option { option_none := node.exprs[0] is ast.None @@ -4879,7 +4909,20 @@ fn (mut g Gen) return_stmt(node ast.Return) { if g.fn_decl.return_type.has_flag(.option) { g.expr_with_opt(node.exprs[0], node.types[0], g.fn_decl.return_type) } else { - g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) + if fn_return_is_fixed_array && !node.types[0].has_flag(.option) { + g.writeln('{0};') + if node.exprs[0] is ast.Ident { + g.write('memcpy(${tmpvar}.ret_arr, ${g.expr_string(node.exprs[0])}, sizeof(${g.typ(node.types[0])})) /*ret*/') + } else { + tmpvar2 := g.new_tmp_var() + g.write('${g.typ(node.types[0])} ${tmpvar2} = ') + g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) + g.writeln(';') + g.write('memcpy(${tmpvar}.ret_arr, ${tmpvar2}, sizeof(${g.typ(node.types[0])})) /*ret*/') + } + } else { + g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) + } } } if use_tmp_var { @@ -5638,7 +5681,8 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) { } ast.ArrayFixed { elem_sym := g.table.sym(sym.info.elem_type) - if !elem_sym.is_builtin() && !sym.info.elem_type.has_flag(.generic) { + if !elem_sym.is_builtin() && !sym.info.elem_type.has_flag(.generic) + && !sym.info.is_fn_ret { // .array_fixed { styp := sym.cname // array_fixed_char_300 => char x[300] diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index a88a86e428..fd7b9171bf 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -225,6 +225,10 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { mut name := g.c_fn_name(node) or { return } mut type_name := g.typ(g.unwrap_generic(node.return_type)) + if node.return_type.has_flag(.generic) && g.table.sym(node.return_type).kind == .array_fixed { + type_name = '_v_${type_name}' + } + if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') && !node.is_main && node.name != 'str' { mut key := node.name diff --git a/vlib/v/gen/c/index.v b/vlib/v/gen/c/index.v index 63741bcf29..cfee3dc7e2 100644 --- a/vlib/v/gen/c/index.v +++ b/vlib/v/gen/c/index.v @@ -368,6 +368,10 @@ fn (mut g Gen) index_of_fixed_array(node ast.IndexExpr, sym ast.TypeSymbol) { } else { g.expr(node.left) } + if node.left is ast.CallExpr && sym.kind == .array_fixed + && (sym.info as ast.ArrayFixed).is_fn_ret { + g.write('.ret_arr') + } } g.write('[') if g.is_direct_array_access || g.pref.translated || node.index is ast.IntegerLiteral { diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index 2043db2c52..6eeff4cca2 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -266,7 +266,7 @@ fn (mut p Parser) comptime_for() ast.ComptimeFor { p.check(.key_in) mut typ_pos := p.tok.pos() lang := p.parse_language() - typ := p.parse_any_type(lang, false, false) + typ := p.parse_any_type(lang, false, false, false) typ_pos = typ_pos.extend(p.prev_tok.pos()) p.check(.dot) for_val := p.check_name() diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index c7294cd77c..0424cf0438 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -10,7 +10,7 @@ import v.token const maximum_inline_sum_type_variants = 3 -pub fn (mut p Parser) parse_array_type(expecting token.Kind) ast.Type { +pub fn (mut p Parser) parse_array_type(expecting token.Kind, is_option bool) ast.Type { p.check(expecting) // fixed array if p.tok.kind in [.number, .name] { @@ -72,7 +72,8 @@ pub fn (mut p Parser) parse_array_type(expecting token.Kind) ast.Type { if fixed_size <= 0 { p.error_with_pos('fixed size cannot be zero or negative', size_expr.pos()) } - idx := p.table.find_or_register_array_fixed(elem_type, fixed_size, size_expr) + idx := p.table.find_or_register_array_fixed(elem_type, fixed_size, size_expr, + !is_option && p.inside_fn_return) if elem_type.has_flag(.generic) { return ast.new_type(idx).set_flag(.generic) } @@ -462,7 +463,7 @@ pub fn (mut p Parser) parse_type() ast.Type { is_array := p.tok.kind == .lsbr pos := p.tok.pos() if p.tok.kind != .lcbr { - typ = p.parse_any_type(language, nr_muls > 0, true) + typ = p.parse_any_type(language, nr_muls > 0, true, is_option) if typ.idx() == 0 { // error is set in parse_type return 0 @@ -501,7 +502,7 @@ If you need to modify an array in a function, use a mutable argument instead: `f return typ } -pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_dot bool) ast.Type { +pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_dot bool, is_option bool) ast.Type { mut name := p.tok.lit if language == .c { name = 'C.${name}' @@ -555,7 +556,7 @@ pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_d } .lsbr, .nilsbr { // array - return p.parse_array_type(p.tok.kind) + return p.parse_array_type(p.tok.kind, is_option) } else { if p.tok.kind == .lpar { diff --git a/vlib/v/tests/fn_fixed_array_ret_test.v b/vlib/v/tests/fn_fixed_array_ret_test.v new file mode 100644 index 0000000000..6e1444dd47 --- /dev/null +++ b/vlib/v/tests/fn_fixed_array_ret_test.v @@ -0,0 +1,90 @@ +fn fixed() [4]int { + return [1, 2, 3, 4]! +} + +fn fixed_opt() ?[4]int { + return [1, 2, 3, 4]! +} + +fn multi_ret() ([4]int, bool) { + return [1, 2, 3, 4]!, true +} + +fn multi_ret_opt() (?[4]int, bool) { + return [1, 2, 3, 4]!, true +} + +fn multi_ret_opt_none() (?[4]int, bool) { + return none, true +} + +fn test_simple() { + a := fixed() + assert a.len == 4 +} + +fn test_simple_option() { + b := fixed_opt() + assert b?.len == 4 +} + +fn test_mr_fixed() { + w, y := multi_ret() + assert w.len == 4 + assert y == true +} + +fn test_mr_fixed_opt() { + w1, y1 := multi_ret_opt() + assert w1?.len == 4 + assert y1 == true +} + +fn test_mr_fixed_opt_none() { + w2, y2 := multi_ret_opt_none() + assert w2 == none + assert y2 == true +} + +fn four(a [4]int) [4]int { + assert a.len == 4 + return a +} + +fn test_passing_arg() { + a := [1, 2, 3, 4]! + b := four(a) + assert b.len == 4 + c := four(b) + assert c.len == 4 +} + +fn test_passing_opt_arg() { + a := fixed_opt() + four(a?) +} + +fn test_assign() { + mut a := [1, 2, 3, 4]! + a = four(a) + assert a.len == 4 + a = fixed_opt()? + assert a.len == 4 + b := a + assert b.len == 4 +} + +fn gn_fixed[T](a [4]T) [4]T { + return a +} + +fn test_generic() { + b := [1, 2, 3, 4]! + a := gn_fixed(b) + c := gn_fixed(a) +} + +fn test_inline() { + mut a := [1, 2, 3, 4]! + assert four(a)[1] == 2 +}