From 88c336512f2eb210ef2e3b379bd802b68bfab448 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 15 Apr 2026 05:42:20 +0300 Subject: [PATCH] checker: implement syntax change for fixed arrays (fixes #25183) --- vlib/v/ast/ast.v | 1 + vlib/v/checker/containers.v | 126 ++++++++--- ...xed_array_new_syntax_size_mismatch_err.out | 5 + ...ixed_array_new_syntax_size_mismatch_err.vv | 3 + vlib/v/fmt/fmt.v | 13 ++ .../fmt/tests/fixed_array_new_syntax_keep.vv | 15 ++ vlib/v/parser/containers.v | 158 +++++++++++++- vlib/v/parser/parse_type.v | 18 +- vlib/v/parser/parser.v | 199 +++++++++--------- .../fixed_array_new_syntax_test.v | 34 +++ 10 files changed, 434 insertions(+), 138 deletions(-) create mode 100644 vlib/v/checker/tests/fixed_array_new_syntax_size_mismatch_err.out create mode 100644 vlib/v/checker/tests/fixed_array_new_syntax_size_mismatch_err.vv create mode 100644 vlib/v/fmt/tests/fixed_array_new_syntax_keep.vv create mode 100644 vlib/v/tests/builtin_arrays/fixed_array_new_syntax_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index c03d7b8e2..16e688c7b 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -1730,6 +1730,7 @@ pub mut: generic_elem_type Type // original generic element type; reused for later concrete instantiations init_type Type // init: value type typ Type // array type + literal_typ Type // array type as written, preserved for fmt generic_typ Type // original generic array type; reused for later concrete instantiations alias_type Type // alias type has_callexpr bool // has expr which needs tmp var to initialize it diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v index f87567f5a..7c370fa3d 100644 --- a/vlib/v/checker/containers.v +++ b/vlib/v/checker/containers.v @@ -14,28 +14,68 @@ fn array_init_result_type(node ast.ArrayInit) ast.Type { } } -fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { - $if trace_ci_fixes ? { - if c.table.cur_fn != unsafe { nil } && c.table.cur_concrete_types.len > 0 - && c.table.cur_fn.name in ['arrays.chunk_while', 'arrays.group_by'] { - eprintln('array_init ${c.table.cur_fn.name} typ=${c.table.type_to_str(node.typ)} elem=${c.table.type_to_str(node.elem_type)} elem_pos=${node.elem_type_pos} exprs=${node.exprs.len} concretes=${c.table.cur_concrete_types.map(c.table.type_to_str(it))}') +fn is_inferred_fixed_array_size_expr(expr ast.Expr) bool { + return expr is ast.RangeExpr && !expr.has_low && !expr.has_high +} + +fn (mut c Checker) fixed_array_contains_inferred_size(typ ast.Type) bool { + mut current_type := typ.clear_option_and_result() + for { + current_sym := c.table.sym(current_type) + if current_sym.kind != .array_fixed { + return false + } + current_info := current_sym.array_fixed_info() + if is_inferred_fixed_array_size_expr(current_info.size_expr) { + return true } + current_type = current_info.elem_type.clear_option_and_result() + } + return false +} + +fn (mut c Checker) resolve_fixed_array_literal_type(typ ast.Type, elem_type ast.Type, expr_count int) ast.Type { + raw_typ := typ.clear_option_and_result() + sym := c.table.sym(raw_typ) + if sym.kind != .array_fixed { + return typ + } + info := sym.array_fixed_info() + mut fixed_size := info.size + mut size_expr := info.size_expr + if is_inferred_fixed_array_size_expr(size_expr) { + fixed_size = expr_count + size_expr = ast.empty_expr + } else if fixed_size <= 0 { + mut mutable_size_expr := size_expr + resolved_typ := c.eval_array_fixed_sizes(mut mutable_size_expr, fixed_size, elem_type) + resolved_info := c.table.sym(resolved_typ).array_fixed_info() + fixed_size = resolved_info.size + size_expr = resolved_info.size_expr } + if fixed_size <= 0 { + c.error('fixed size cannot be zero or negative (fixed_size: ${fixed_size})', + size_expr.pos()) + return typ + } + idx := c.table.find_or_register_array_fixed(elem_type, fixed_size, size_expr, info.is_fn_ret) + mut resolved_typ := ast.new_type(idx) + if typ.has_flag(.generic) || elem_type.has_flag(.generic) { + resolved_typ = resolved_typ.set_flag(.generic) + } + if typ.has_flag(.option) { + resolved_typ = resolved_typ.set_flag(.option) + } + return resolved_typ +} + +fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { is_inferred_array_literal := node.exprs.len > 0 && !node.is_fixed && !node.has_cap && !node.has_len && !node.has_init && node.elem_type_pos.pos == node.pos.pos && node.generic_typ == 0 && node.generic_elem_type == 0 if c.has_active_generic_recheck_context() { is_untyped_empty_array := node.exprs.len == 0 && !node.is_fixed && !node.has_cap && !node.has_len && !node.has_init && node.elem_type_pos.pos == node.pos.pos - $if trace_ci_fixes ? { - if c.file.path.contains('/eventbus/') { - eprintln('array_init file=${c.file.path} fn=${if c.table.cur_fn == unsafe { nil } { - '' - } else { - c.table.cur_fn.name - }} active=${c.has_active_generic_recheck_context()} expected=${c.table.type_to_str(c.expected_type)} typ=${c.table.type_to_str(node.typ)} elem=${c.table.type_to_str(node.elem_type)} empty=${is_untyped_empty_array}') - } - } if node.generic_typ == 0 && node.typ != ast.void_type && (node.typ.has_flag(.generic) || c.type_has_unresolved_generic_parts(node.typ)) { node.generic_typ = node.typ @@ -59,11 +99,6 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { } else {} } - $if trace_ci_fixes ? { - if c.file.path.contains('/eventbus/') { - eprintln('array_init expected apply expected=${c.table.type_to_str(expected_array_typ)} new_typ=${c.table.type_to_str(node.typ)} new_elem=${c.table.type_to_str(node.elem_type)}') - } - } } base_node_typ := if node.generic_typ != 0 { node.generic_typ } else { node.typ } base_elem_type := if node.generic_elem_type != 0 { @@ -95,11 +130,6 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { node.typ = ast.void_type node.elem_type = ast.void_type } - $if trace_ci_fixes ? { - if c.table.cur_fn.name in ['arrays.chunk_while', 'arrays.group_by'] { - eprintln('array_init reset ${c.table.cur_fn.name} concretes=${c.table.cur_concrete_types.map(c.table.type_to_str(it))}') - } - } } mut elem_type := ast.void_type unwrap_elem_type := c.unwrap_generic(node.elem_type) @@ -206,7 +236,53 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { c.check_elements_ref_fields_initialized(unwrap_elem_type, node.pos) } // T{0} initialization when T is an array - if !node.is_fixed && node.expr_types.len == 0 { + if node.is_fixed && node.has_val && node.expr_types.len == 0 { + if c.table.final_sym(node.elem_type).kind == .array_fixed { + elem_info := c.table.final_sym(node.elem_type).array_fixed_info() + if c.array_fixed_has_unresolved_size(elem_info) + && !c.fixed_array_contains_inferred_size(node.elem_type) { + node.elem_type = c.resolve_fixed_array_literal_type(node.elem_type, + elem_info.elem_type, 0) + } + } + mut expected_elem_type := node.elem_type + mut should_infer_fixed_elem_type := + c.table.final_sym(expected_elem_type).kind == .array_fixed + && c.fixed_array_contains_inferred_size(expected_elem_type) + for i, mut expr in node.exprs { + old_expected_type := c.expected_type + if should_infer_fixed_elem_type && i == 0 { + c.expected_type = ast.void_type + } else { + c.expected_type = expected_elem_type + } + mut typ := c.check_expr_option_or_result_call(expr, c.expr(mut expr)) + c.expected_type = old_expected_type + if expr is ast.CallExpr { + ret_sym := c.table.sym(typ) + if ret_sym.kind == .array_fixed { + typ = c.cast_fixed_array_ret(typ, ret_sym) + } + node.has_callexpr = true + } + if should_infer_fixed_elem_type && i == 0 + && c.table.final_sym(typ).kind == .array_fixed { + expected_elem_type = typ + node.elem_type = typ + should_infer_fixed_elem_type = false + } + c.check_expected(typ, expected_elem_type) or { + c.error('invalid array element: ${err.msg()}', expr.pos()) + } + node.expr_types << typ + } + node.typ = c.resolve_fixed_array_literal_type(node.typ, node.elem_type, node.exprs.len) + resolved_info := c.table.sym(node.typ.clear_option_and_result()).array_fixed_info() + if resolved_info.size != node.exprs.len { + c.error('fixed array expects ${resolved_info.size} value(s), but got ${node.exprs.len}', + node.pos) + } + } else if !node.is_fixed && node.expr_types.len == 0 { for mut expr in node.exprs { typ := c.expr(mut expr) c.check_expected(typ, node.elem_type) or { diff --git a/vlib/v/checker/tests/fixed_array_new_syntax_size_mismatch_err.out b/vlib/v/checker/tests/fixed_array_new_syntax_size_mismatch_err.out new file mode 100644 index 000000000..4d8102078 --- /dev/null +++ b/vlib/v/checker/tests/fixed_array_new_syntax_size_mismatch_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/fixed_array_new_syntax_size_mismatch_err.vv:2:13: error: fixed array expects 2 value(s), but got 3 + 1 | fn main() { + 2 | _ := [2]int[1 2 3] + | ~~~~~~~ + 3 | } diff --git a/vlib/v/checker/tests/fixed_array_new_syntax_size_mismatch_err.vv b/vlib/v/checker/tests/fixed_array_new_syntax_size_mismatch_err.vv new file mode 100644 index 000000000..0547a34fb --- /dev/null +++ b/vlib/v/checker/tests/fixed_array_new_syntax_size_mismatch_err.vv @@ -0,0 +1,3 @@ +fn main() { + _ := [2]int[1 2 3] +} diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index cac03a0a3..f7d4f724a 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1849,6 +1849,8 @@ pub fn (mut f Fmt) array_decompose(node ast.ArrayDecompose) { } pub fn (mut f Fmt) array_init(node ast.ArrayInit) { + typed_fixed_literal := node.is_fixed && node.has_val && node.typ != 0 + && node.typ != ast.void_type if node.is_fixed && node.is_option { f.write('?') } @@ -1884,6 +1886,14 @@ pub fn (mut f Fmt) array_init(node ast.ArrayInit) { f.write('}') return } + if typed_fixed_literal && f.array_init_depth == 0 { + fixed_literal_type := if node.literal_typ != ast.void_type { + node.literal_typ + } else { + node.typ.clear_option_and_result() + } + f.write(f.type_to_str_using_aliases(fixed_literal_type, f.mod2alias)) + } // `[1,2,3]` f.write('[') mut inc_indent := false @@ -2044,6 +2054,9 @@ pub fn (mut f Fmt) array_init(node ast.ArrayInit) { // `[100]u8` if node.is_fixed { if node.has_val { + if typed_fixed_literal { + return + } if node.from_to_fixed_size { f.write('.to_fixed_size()') } else { diff --git a/vlib/v/fmt/tests/fixed_array_new_syntax_keep.vv b/vlib/v/fmt/tests/fixed_array_new_syntax_keep.vv new file mode 100644 index 000000000..288baf07c --- /dev/null +++ b/vlib/v/fmt/tests/fixed_array_new_syntax_keep.vv @@ -0,0 +1,15 @@ +const rows = 2 +const cols = 2 + +fn main() { + row1 := [..]int[1, 2] + row2 := [..]int[3, 4] + _ := [..][..]int[ + row1, + row2, + ] + _ = [rows][cols]int[ + row1, + row2, + ] +} diff --git a/vlib/v/parser/containers.v b/vlib/v/parser/containers.v index cacfead56..bdeac75a8 100644 --- a/vlib/v/parser/containers.v +++ b/vlib/v/parser/containers.v @@ -6,6 +6,124 @@ module parser import v.ast import v.token +fn is_inferred_fixed_array_size_expr(expr ast.Expr) bool { + return expr is ast.RangeExpr && !expr.has_low && !expr.has_high +} + +fn (mut p Parser) parse_fixed_array_literal_elem_type() ast.Type { + elem_type_pos := p.tok.pos() + if p.tok.kind == .name && p.tok.lit == 'byte' { + p.error_with_pos('`byte` has been deprecated in favor of `u8`: use `[10]u8{}` instead of `[10]byte{}`', + elem_type_pos) + } + old_allow_auto_fixed_array_size := p.allow_auto_fixed_array_size + p.allow_auto_fixed_array_size = true + elem_type := p.parse_type() + p.allow_auto_fixed_array_size = old_allow_auto_fixed_array_size + if elem_type != 0 { + s := p.table.sym(elem_type) + if s.name == 'byte' { + p.error('`byte` has been deprecated in favor of `u8`: use `[10]u8{}` instead of `[10]byte{}`') + } + } + if elem_type == ast.chan_type { + p.chan_type_error() + return 0 + } + return elem_type +} + +fn (mut p Parser) fixed_array_literal_type(size_expr ast.Expr, elem_type ast.Type) ast.Type { + mut fixed_size := 0 + mut size_unresolved := true + if !is_inferred_fixed_array_size_expr(size_expr) { + if p.pref.is_fmt { + fixed_size = 987654321 + } else { + mut mutable_size_expr := size_expr + fixed_size, size_unresolved = p.eval_array_fixed_sizes(mut mutable_size_expr) + } + } + if fixed_size <= 0 && !size_unresolved { + 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, false) + mut array_type := ast.new_type(idx) + if elem_type.has_flag(.generic) { + array_type = array_type.set_flag(.generic) + } + return array_type +} + +fn (mut p Parser) parse_fixed_array_literal_values(array_type ast.Type, is_option bool) ast.ArrayInit { + first_pos := p.tok.pos() + mut last_pos := first_pos + raw_array_type := array_type.clear_option_and_result() + array_info := p.table.sym(raw_array_type).array_fixed_info() + mut elem_type := array_info.elem_type + mut exprs := []ast.Expr{} + mut ecmnts := [][]ast.Comment{} + mut pre_cmnts := []ast.Comment{} + p.check(.lsbr) + old_inside_array_lit := p.inside_array_lit + old_last_enum_name := p.last_enum_name + old_last_enum_mod := p.last_enum_mod + p.inside_array_lit = true + p.last_enum_name = '' + p.last_enum_mod = '' + pre_cmnts = p.eat_comments() + for p.tok.kind !in [.rsbr, .eof] { + exprs << if p.table.final_sym(elem_type).kind == .array_fixed && p.tok.kind == .lsbr { + ast.Expr(p.parse_fixed_array_literal_values(elem_type, false)) + } else { + p.expr(0) + } + ecmnts << p.eat_comments() + if p.tok.kind == .comma { + p.next() + } + ecmnts.last() << p.eat_comments() + } + p.inside_array_lit = old_inside_array_lit + p.last_enum_name = old_last_enum_name + p.last_enum_mod = old_last_enum_mod + last_pos = p.tok.pos() + p.check(.rsbr) + if exprs.len > 0 && p.table.final_sym(elem_type).kind == .array_fixed + && exprs[0] is ast.ArrayInit { + first_expr := exprs[0] as ast.ArrayInit + elem_type = first_expr.typ + } + mut final_array_type := raw_array_type + if is_inferred_fixed_array_size_expr(array_info.size_expr) && exprs.len > 0 { + idx := p.table.find_or_register_array_fixed(elem_type, exprs.len, ast.empty_expr, + array_info.is_fn_ret) + final_array_type = ast.new_type(idx) + } else if elem_type != array_info.elem_type { + idx := p.table.find_or_register_array_fixed(elem_type, array_info.size, + array_info.size_expr, array_info.is_fn_ret) + final_array_type = ast.new_type(idx) + } + if is_option { + final_array_type = final_array_type.set_flag(.option) + } + return ast.ArrayInit{ + pos: first_pos.extend_with_last_line(last_pos, p.prev_tok.line_nr) + mod: p.mod + ecmnts: ecmnts + pre_cmnts: pre_cmnts + is_fixed: true + is_option: is_option + has_val: true + exprs: exprs + elem_type_pos: first_pos + elem_type: elem_type + typ: final_array_type + literal_typ: raw_array_type + alias_type: ast.void_type + } +} + fn (mut p Parser) array_init(is_option bool, alias_array_type ast.Type) ast.ArrayInit { first_pos := p.tok.pos() mut last_pos := p.tok.pos() @@ -63,7 +181,18 @@ fn (mut p Parser) array_init(is_option bool, alias_array_type ast.Type) ast.Arra p.last_enum_mod = '' pre_cmnts = p.eat_comments() for i := 0; p.tok.kind !in [.rsbr, .eof]; i++ { - exprs << p.expr(0) + exprs << if p.tok.kind == .dotdot && p.peek_tok.kind == .rsbr { + ast.Expr(ast.RangeExpr{ + pos: p.tok.pos() + low: ast.empty_expr + high: ast.empty_expr + }) + } else { + p.expr(0) + } + if p.tok.kind == .dotdot && p.peek_tok.kind == .rsbr { + p.next() + } ecmnts << p.eat_comments() if p.tok.kind == .comma { p.next() @@ -79,17 +208,19 @@ fn (mut p Parser) array_init(is_option bool, alias_array_type ast.Type) ast.Arra if exprs.len == 1 && p.tok.line_nr == line_nr && (p.tok.kind in [.name, .amp, .lpar, .question, .key_shared] || (p.tok.kind == .lsbr && p.is_array_type())) { - // [100]u8 - elem_type = p.parse_type() - if elem_type != 0 { - s := p.table.sym(elem_type) - if s.name == 'byte' { - p.error('`byte` has been deprecated in favor of `u8`: use `[10]u8{}` instead of `[10]byte{}`') - } - } + // [100]u8{} or [100]u8[1 2 3] + elem_type = p.parse_fixed_array_literal_elem_type() last_pos = p.tok.pos() is_fixed = true - if p.tok.kind == .lcbr { + if p.tok.kind == .lsbr { + array_type = p.fixed_array_literal_type(exprs[0], elem_type) + return p.parse_fixed_array_literal_values(array_type, is_option) + } else if p.tok.kind == .lcbr { + if is_inferred_fixed_array_size_expr(exprs[0]) { + p.error_with_pos('`[..]Type` requires a value list like `[..]Type[...]`', + first_pos.extend(last_pos)) + return ast.ArrayInit{} + } p.next() if p.tok.kind != .rcbr { pos := p.tok.pos() @@ -109,7 +240,14 @@ fn (mut p Parser) array_init(is_option bool, alias_array_type ast.Type) ast.Arra } last_pos = p.tok.pos() p.check(.rcbr) + array_type = ast.void_type } else { + if is_inferred_fixed_array_size_expr(exprs[0]) { + p.error_with_pos('`[..]Type` requires a value list like `[..]Type[...]`', + first_pos.extend(last_pos)) + return ast.ArrayInit{} + } + array_type = ast.void_type modifier := if is_option { '?' } else { '' } p.warn_with_pos('use e.g. `x := ${modifier}[1]Type{}` instead of `x := ${modifier}[1]Type`', first_pos.extend(last_pos)) diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 76363f378..dcfba6a0c 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -109,12 +109,22 @@ fn (mut p Parser) parse_array_type(expecting token.Kind, is_option bool) ast.Typ // fixed array if p.tok.kind != .rsbr { mut fixed_size := 0 - mut size_expr := p.expr(0) mut size_unresolved := true - if p.pref.is_fmt { - fixed_size = 987654321 + mut size_expr := ast.empty_expr + if p.allow_auto_fixed_array_size && p.tok.kind == .dotdot && p.peek_tok.kind == .rsbr { + size_expr = ast.RangeExpr{ + pos: p.tok.pos() + low: ast.empty_expr + high: ast.empty_expr + } + p.next() } else { - fixed_size, size_unresolved = p.eval_array_fixed_sizes(mut size_expr) + size_expr = p.expr(0) + if p.pref.is_fmt { + fixed_size = 987654321 + } else { + fixed_size, size_unresolved = p.eval_array_fixed_sizes(mut size_expr) + } } p.check(.rsbr) p.fixed_array_dim++ diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index bb4f5d5ad..4582e60b9 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -25,105 +25,106 @@ mut: unique_prefix string // a hash of p.file_path, used for making anon fn generation unique file_backend_mode ast.Language // .c for .c.v|.c.vv|.c.vsh files; .js for .js.v files, .amd64/.rv32/other arches for .amd64.v/.rv32.v/etc. files, .v otherwise. // see comment in parse_file - tok token.Token - prev_tok token.Token - peek_tok token.Token - language ast.Language - fn_language ast.Language // .c for `fn C.abcd()` declarations - struct_language ast.Language // for `struct C.abcd{ embedded struct/union }` declarations - expr_level int // prevent too deep recursions for pathological programs - inside_vlib_file bool // true for all vlib/ files - inside_test_file bool // when inside _test.v or _test.vv file - inside_if bool - inside_comptime_if bool - inside_if_expr bool - inside_if_cond bool - inside_ct_if_expr bool - inside_or_expr bool - inside_for bool - inside_for_expr bool - inside_fn bool // true even with implicit main - inside_fn_return bool - inside_fn_param bool // true while parsing function parameter types - inside_fn_concrete_type bool // parsing fn_name[concrete_type]() call expr - inside_call_args bool // true inside f( .... ) - inside_unsafe_fn bool - inside_str_interp bool - inside_array_lit bool - inside_in_array bool - inside_infix bool - inside_assign_rhs bool // rhs assignment - inside_match bool // to separate `match A { }` from `Struct{}` - inside_select bool // to allow `ch <- Struct{} {` inside `select` - inside_match_case bool // to separate `match_expr { }` from `Struct{}` - inside_match_body bool // to fix eval not used TODO - inside_ct_match bool - inside_ct_match_case bool - inside_ct_match_body bool - inside_unsafe bool - inside_sum_type bool // to prevent parsing inline sum type again - inside_asm_template bool - inside_asm bool - inside_defer bool - defer_mode ast.DeferMode - inside_generic_params bool // indicates if parsing between `<` and `>` of a method/function - inside_receiver_param bool // indicates if parsing the receiver parameter inside the first `(` and `)` of a method - inside_struct_field_decl bool - inside_struct_attr_decl bool - inside_map_init bool - inside_orm bool - inside_chan_decl bool - inside_attr_decl bool - inside_lock_exprs bool - array_dim int // array dim parsing level - fixed_array_dim int // fixed array dim parsing level - or_is_handled bool // ignore `or` in this expression - builtin_mod bool // are we in the `builtin` module? - mod string // current module name - is_manualfree bool // true when `@[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree - has_globals bool // `@[has_globals] module abc` - allow globals declarations, even without -enable-globals, in that single .v file __only__ - is_generated bool // `@[generated] module abc` - turn off compiler notices for that single .v file __only__. - is_translated bool // `@[translated] module abc` - mark a file as translated, to relax some compiler checks for translated code. - attrs []ast.Attr // attributes before next decl stmt - expr_mod string // for constructing full type names in parse_type() - last_enum_name string // saves the last enum name on an array initialization - last_enum_mod string // saves the last enum mod name on an array initialization - imports map[string]string // alias => mod_name - ast_imports []ast.Import // mod_names - used_imports []string - auto_imports []string // imports, the user does not need to specify - implied_imports []string // ​imports that the user's code uses but omitted to import explicitly, used by `vfmt` - imported_symbols map[string]string - imported_symbols_used map[string]bool - imported_symbols_trie token.KeywordsMatcherTrie - is_amp bool // for generating the right code for `&Foo{}` - returns bool - is_stmt_ident bool // true while the beginning of a statement is an ident/selector - expecting_type bool // `is Type`, expecting type - expecting_value bool = true // true where a node value will be used - cur_fn_name string - cur_fn_scope &ast.Scope = unsafe { nil } - label_names []string - name_error bool // indicates if the token is not a name or the name is on another line - n_asm int // controls assembly labels - global_labels []string - comptime_if_cond bool - defer_vars []ast.Ident - should_abort bool // when too many errors/warnings/notices are accumulated, should_abort becomes true, and the parser should stop - codegen_text string - anon_struct_decl ast.StructDecl - init_generic_types []ast.Type - consume_init_generic_types bool - if_cond_comments []ast.Comment - left_comments []ast.Comment - script_mode bool - script_mode_start_token token.Token - generic_type_level int // to avoid infinite recursion segfaults due to compiler bugs in ensure_type_exists - main_already_defined bool // TODO move to checker - is_vls bool - is_vls_skip_file bool // in `vls` mode, skip parse and check for unrelated files, such as `vlib` - inside_import_section bool - cur_comments []ast.Comment // comments between other stmts + tok token.Token + prev_tok token.Token + peek_tok token.Token + language ast.Language + fn_language ast.Language // .c for `fn C.abcd()` declarations + struct_language ast.Language // for `struct C.abcd{ embedded struct/union }` declarations + expr_level int // prevent too deep recursions for pathological programs + inside_vlib_file bool // true for all vlib/ files + inside_test_file bool // when inside _test.v or _test.vv file + inside_if bool + inside_comptime_if bool + inside_if_expr bool + inside_if_cond bool + inside_ct_if_expr bool + inside_or_expr bool + inside_for bool + inside_for_expr bool + inside_fn bool // true even with implicit main + inside_fn_return bool + inside_fn_param bool // true while parsing function parameter types + inside_fn_concrete_type bool // parsing fn_name[concrete_type]() call expr + inside_call_args bool // true inside f( .... ) + inside_unsafe_fn bool + inside_str_interp bool + inside_array_lit bool + inside_in_array bool + inside_infix bool + inside_assign_rhs bool // rhs assignment + inside_match bool // to separate `match A { }` from `Struct{}` + inside_select bool // to allow `ch <- Struct{} {` inside `select` + inside_match_case bool // to separate `match_expr { }` from `Struct{}` + inside_match_body bool // to fix eval not used TODO + inside_ct_match bool + inside_ct_match_case bool + inside_ct_match_body bool + inside_unsafe bool + inside_sum_type bool // to prevent parsing inline sum type again + inside_asm_template bool + inside_asm bool + inside_defer bool + defer_mode ast.DeferMode + inside_generic_params bool // indicates if parsing between `<` and `>` of a method/function + inside_receiver_param bool // indicates if parsing the receiver parameter inside the first `(` and `)` of a method + inside_struct_field_decl bool + inside_struct_attr_decl bool + inside_map_init bool + inside_orm bool + inside_chan_decl bool + inside_attr_decl bool + inside_lock_exprs bool + array_dim int // array dim parsing level + fixed_array_dim int // fixed array dim parsing level + allow_auto_fixed_array_size bool // allow `[..]` while parsing fixed array literal types + or_is_handled bool // ignore `or` in this expression + builtin_mod bool // are we in the `builtin` module? + mod string // current module name + is_manualfree bool // true when `@[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree + has_globals bool // `@[has_globals] module abc` - allow globals declarations, even without -enable-globals, in that single .v file __only__ + is_generated bool // `@[generated] module abc` - turn off compiler notices for that single .v file __only__. + is_translated bool // `@[translated] module abc` - mark a file as translated, to relax some compiler checks for translated code. + attrs []ast.Attr // attributes before next decl stmt + expr_mod string // for constructing full type names in parse_type() + last_enum_name string // saves the last enum name on an array initialization + last_enum_mod string // saves the last enum mod name on an array initialization + imports map[string]string // alias => mod_name + ast_imports []ast.Import // mod_names + used_imports []string + auto_imports []string // imports, the user does not need to specify + implied_imports []string // ​imports that the user's code uses but omitted to import explicitly, used by `vfmt` + imported_symbols map[string]string + imported_symbols_used map[string]bool + imported_symbols_trie token.KeywordsMatcherTrie + is_amp bool // for generating the right code for `&Foo{}` + returns bool + is_stmt_ident bool // true while the beginning of a statement is an ident/selector + expecting_type bool // `is Type`, expecting type + expecting_value bool = true // true where a node value will be used + cur_fn_name string + cur_fn_scope &ast.Scope = unsafe { nil } + label_names []string + name_error bool // indicates if the token is not a name or the name is on another line + n_asm int // controls assembly labels + global_labels []string + comptime_if_cond bool + defer_vars []ast.Ident + should_abort bool // when too many errors/warnings/notices are accumulated, should_abort becomes true, and the parser should stop + codegen_text string + anon_struct_decl ast.StructDecl + init_generic_types []ast.Type + consume_init_generic_types bool + if_cond_comments []ast.Comment + left_comments []ast.Comment + script_mode bool + script_mode_start_token token.Token + generic_type_level int // to avoid infinite recursion segfaults due to compiler bugs in ensure_type_exists + main_already_defined bool // TODO move to checker + is_vls bool + is_vls_skip_file bool // in `vls` mode, skip parse and check for unrelated files, such as `vlib` + inside_import_section bool + cur_comments []ast.Comment // comments between other stmts pub mut: scanner &scanner.Scanner = unsafe { nil } table &ast.Table = unsafe { nil } diff --git a/vlib/v/tests/builtin_arrays/fixed_array_new_syntax_test.v b/vlib/v/tests/builtin_arrays/fixed_array_new_syntax_test.v new file mode 100644 index 000000000..1b62c88b0 --- /dev/null +++ b/vlib/v/tests/builtin_arrays/fixed_array_new_syntax_test.v @@ -0,0 +1,34 @@ +const fixed_array_new_syntax_rows = 2 +const fixed_array_new_syntax_cols = 2 + +fn test_fixed_array_new_syntax_1d() { + arr1 := [4]f32[1, 2, 3, 4] + arr2 := [..]f32[1, 2, 3, 4] + assert arr1 == [f32(1), 2, 3, 4]! + assert arr2 == arr1 +} + +fn test_fixed_array_new_syntax_2d() { + arr1 := [2][2]int[ + [1, 2], + [3, 4], + ] + arr2 := [..][..]int[ + [1, 2], + [3, 4], + ] + assert arr1[0][0] == 1 + assert arr1[1][1] == 4 + assert arr2 == arr1 +} + +fn test_fixed_array_new_syntax_with_consts_and_idents() { + row1 := [..]int[1, 2] + row2 := [..]int[3, 4] + arr := [fixed_array_new_syntax_rows][fixed_array_new_syntax_cols]int[ + row1, + row2, + ] + assert arr[0] == row1 + assert arr[1] == row2 +} -- 2.39.5