Skip to content

Commit

Permalink
ast, parser, fmt: fix alias array no cast init (#20898)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuyi98 committed Feb 24, 2024
1 parent cf7dcfe commit 1733dab
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 92 deletions.
1 change: 1 addition & 0 deletions vlib/v/ast/ast.v
Expand Up @@ -1492,6 +1492,7 @@ pub mut:
elem_type Type // element type
init_type Type // init: value type
typ Type // array type
alias_type Type // alias type
}

pub struct ArrayDecompose {
Expand Down
6 changes: 5 additions & 1 deletion vlib/v/fmt/fmt.v
Expand Up @@ -1710,7 +1710,11 @@ pub fn (mut f Fmt) array_init(node ast.ArrayInit) {
if node.exprs.len == 0 && node.typ != 0 && node.typ != ast.void_type {
// `x := []string{}`
f.mark_types_import_as_used(node.typ)
f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias))
if node.alias_type != ast.void_type {
f.write(f.table.type_to_str_using_aliases(node.alias_type, f.mod2alias))
} else {
f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias))
}
f.write('{')
if node.has_len {
f.write('len: ')
Expand Down
183 changes: 96 additions & 87 deletions vlib/v/parser/containers.v
Expand Up @@ -6,10 +6,9 @@ module parser
import v.ast
import v.token

fn (mut p Parser) array_init(is_option bool) ast.ArrayInit {
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()
p.check(.lsbr)
mut array_type := ast.void_type
mut elem_type := ast.void_type
mut elem_type_pos := first_pos
Expand All @@ -22,103 +21,112 @@ fn (mut p Parser) array_init(is_option bool) ast.ArrayInit {
mut has_init := false
mut has_index := false
mut init_expr := ast.empty_expr
if p.tok.kind == .rsbr {
last_pos = p.tok.pos()
// []typ => `[]` and `typ` must be on the same line
line_nr := p.tok.line_nr
p.next()
// []string
if p.tok.kind in [.name, .amp, .lsbr, .question, .key_shared] && p.tok.line_nr == line_nr {
elem_type_pos = p.tok.pos()
elem_type = p.parse_type()
// this is set here because it's a known type, others could be the
// result of expr so we do those in checker
if elem_type != 0 {
idx := p.table.find_or_register_array(elem_type)
if elem_type.has_flag(.generic) {
array_type = ast.new_type(idx).set_flag(.generic)
} else {
array_type = ast.new_type(idx)
}
if is_option {
array_type = array_type.set_flag(.option)
if alias_array_type == ast.void_type {
p.check(.lsbr)
if p.tok.kind == .rsbr {
last_pos = p.tok.pos()
// []typ => `[]` and `typ` must be on the same line
line_nr := p.tok.line_nr
p.next()
// []string
if p.tok.kind in [.name, .amp, .lsbr, .question, .key_shared]
&& p.tok.line_nr == line_nr {
elem_type_pos = p.tok.pos()
elem_type = p.parse_type()
// this is set here because it's a known type, others could be the
// result of expr so we do those in checker
if elem_type != 0 {
idx := p.table.find_or_register_array(elem_type)
if elem_type.has_flag(.generic) {
array_type = ast.new_type(idx).set_flag(.generic)
} else {
array_type = ast.new_type(idx)
}
if is_option {
array_type = array_type.set_flag(.option)
}
has_type = true
}
has_type = true
}
}
last_pos = p.tok.pos()
} else {
// [1,2,3] or [const]u8
old_inside_array_lit := p.inside_array_lit
p.inside_array_lit = true
pre_cmnts = p.eat_comments()
for i := 0; p.tok.kind !in [.rsbr, .eof]; i++ {
exprs << 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
line_nr := p.tok.line_nr
last_pos = p.tok.pos()
p.check(.rsbr)
if exprs.len == 1 && p.tok.line_nr == line_nr
&& (p.tok.kind in [.name, .amp, .question, .key_shared]
|| (p.tok.kind == .lsbr && p.is_array_type())) {
// [100]u8
elem_type = p.parse_type()
if p.table.sym(elem_type).name == 'byte' {
p.error('`byte` has been deprecated in favor of `u8`: use `[10]u8{}` instead of `[10]byte{}`')
last_pos = p.tok.pos()
} else {
// [1,2,3] or [const]u8
old_inside_array_lit := p.inside_array_lit
p.inside_array_lit = true
pre_cmnts = p.eat_comments()
for i := 0; p.tok.kind !in [.rsbr, .eof]; i++ {
exprs << 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
line_nr := p.tok.line_nr
last_pos = p.tok.pos()
is_fixed = true
if p.tok.kind == .lcbr {
p.next()
if p.tok.kind != .rcbr {
pos := p.tok.pos()
n := p.check_name()
if n != 'init' {
if is_fixed {
p.error_with_pos('`len` and `cap` are invalid attributes for fixed array dimension',
pos)
} else {
p.error_with_pos('expected `init:`, not `${n}`', pos)
p.check(.rsbr)
if exprs.len == 1 && p.tok.line_nr == line_nr
&& (p.tok.kind in [.name, .amp, .question, .key_shared]
|| (p.tok.kind == .lsbr && p.is_array_type())) {
// [100]u8
elem_type = p.parse_type()
if p.table.sym(elem_type).name == 'byte' {
p.error('`byte` has been deprecated in favor of `u8`: use `[10]u8{}` instead of `[10]byte{}`')
}
last_pos = p.tok.pos()
is_fixed = true
if p.tok.kind == .lcbr {
p.next()
if p.tok.kind != .rcbr {
pos := p.tok.pos()
n := p.check_name()
if n != 'init' {
if is_fixed {
p.error_with_pos('`len` and `cap` are invalid attributes for fixed array dimension',
pos)
} else {
p.error_with_pos('expected `init:`, not `${n}`', pos)
}
return ast.ArrayInit{}
}
return ast.ArrayInit{}
p.check(.colon)
has_init = true
has_index = p.handle_index_variable(mut init_expr)
}
p.check(.colon)
has_init = true
has_index = p.handle_index_variable(mut init_expr)
last_pos = p.tok.pos()
p.check(.rcbr)
} else {
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))
}
last_pos = p.tok.pos()
p.check(.rcbr)
} else {
if p.tok.kind == .not {
last_pos = p.tok.pos()
is_fixed = true
has_val = true
p.next()
}
if p.tok.kind == .not && p.tok.line_nr == p.prev_tok.line_nr {
last_pos = p.tok.pos()
p.error_with_pos('use e.g. `[1, 2, 3]!` instead of `[1, 2, 3]!!`',
last_pos)
p.next()
}
}
}
if exprs.len == 0 && p.tok.kind != .lcbr && has_type {
if !p.pref.is_fmt {
modifier := if is_option { '?' } else { '' }
p.warn_with_pos('use e.g. `x := ${modifier}[1]Type{}` instead of `x := ${modifier}[1]Type`',
p.warn_with_pos('use `x := ${modifier}[]Type{}` instead of `x := ${modifier}[]Type`',
first_pos.extend(last_pos))
}
} else {
if p.tok.kind == .not {
last_pos = p.tok.pos()
is_fixed = true
has_val = true
p.next()
}
if p.tok.kind == .not && p.tok.line_nr == p.prev_tok.line_nr {
last_pos = p.tok.pos()
p.error_with_pos('use e.g. `[1, 2, 3]!` instead of `[1, 2, 3]!!`', last_pos)
p.next()
}
}
}
if exprs.len == 0 && p.tok.kind != .lcbr && has_type {
if !p.pref.is_fmt {
modifier := if is_option { '?' } else { '' }
p.warn_with_pos('use `x := ${modifier}[]Type{}` instead of `x := ${modifier}[]Type`',
first_pos.extend(last_pos))
}
} else {
array_type = (p.table.sym(alias_array_type).info as ast.Alias).parent_type
elem_type = p.table.sym(array_type).array_info().elem_type
p.next()
}
mut has_len := false
mut has_cap := false
Expand Down Expand Up @@ -170,6 +178,7 @@ fn (mut p Parser) array_init(is_option bool) ast.ArrayInit {
mod: p.mod
elem_type: elem_type
typ: array_type
alias_type: alias_array_type
exprs: exprs
ecmnts: ecmnts
pre_cmnts: pre_cmnts
Expand Down
4 changes: 2 additions & 2 deletions vlib/v/parser/expr.v
Expand Up @@ -216,10 +216,10 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr {
pos: pos
}
} else {
node = p.array_init(false)
node = p.array_init(false, ast.void_type)
}
} else {
node = p.array_init(false)
node = p.array_init(false, ast.void_type)
}
}
.key_none {
Expand Down
23 changes: 21 additions & 2 deletions vlib/v/parser/parser.v
Expand Up @@ -2543,6 +2543,19 @@ fn (mut p Parser) is_generic_cast() bool {
return false
}

fn (mut p Parser) alias_array_type() ast.Type {
full_name := p.prepend_mod(p.tok.lit)
if idx := p.table.type_idxs[full_name] {
sym := p.table.sym(idx)
if sym.info is ast.Alias {
if p.table.sym(sym.info.parent_type).kind == .array {
return idx
}
}
}
return ast.void_type
}

@[direct_array_access]
fn (mut p Parser) name_expr() ast.Expr {
prev_tok_kind := p.prev_tok.kind
Expand Down Expand Up @@ -2840,7 +2853,13 @@ fn (mut p Parser) name_expr() ast.Expr {
&& (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital))
&& !p.inside_match_case && (!p.inside_if || p.inside_select)
&& (!p.inside_for || p.inside_select) {
return p.struct_init(p.mod + '.' + p.tok.lit, .normal, is_option) // short_syntax: false
alias_array_type := p.alias_array_type()
if alias_array_type != ast.void_type {
return p.array_init(is_option, alias_array_type)
} else {
// `if a == Foo{} {...}` or `match foo { Foo{} {...} }`
return p.struct_init(p.mod + '.' + p.tok.lit, .normal, is_option)
}
} else if p.peek_tok.kind == .lcbr
&& ((p.inside_if && lit0_is_capital && p.tok.lit.len > 1 && !known_var && language == .v)
|| (p.inside_match_case && p.tok.kind == .name && p.peek_tok.is_next_to(p.tok))) {
Expand Down Expand Up @@ -2957,7 +2976,7 @@ fn (mut p Parser) name_expr() ast.Expr {
p.expr_mod = ''
return node
} else if is_option && p.tok.kind == .lsbr {
return p.array_init(is_option)
return p.array_init(is_option, ast.void_type)
} else if !known_var && language == .v && p.peek_tok.kind == .dot && !p.pref.is_fmt {
peek_tok2 := p.peek_token(2)
peek_tok3 := p.peek_token(3)
Expand Down
12 changes: 12 additions & 0 deletions vlib/v/tests/alias_array_no_cast_init_test.v
@@ -0,0 +1,12 @@
pub type Labels = [][]int

pub fn new_labels(width int, height int) Labels {
mut labels := Labels{len: height, init: []int{len: width}}
return labels
}

fn test_alias_array_no_cast_init() {
mut labels := new_labels(2, 2)
println(labels)
assert labels == [[0, 0], [0, 0]]
}

0 comments on commit 1733dab

Please sign in to comment.