1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

all: allow fixed array returns (#17931)

This commit is contained in:
Felipe Pena 2023-05-18 06:30:49 -03:00 committed by GitHub
parent c8d2098a14
commit 9d9785cc05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 236 additions and 60 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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