Skip to content

Commit

Permalink
parser: fix fixed array with eval const size (#19269)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuyi98 committed Sep 4, 2023
1 parent d04a3e5 commit e6b7eb3
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 27 deletions.
91 changes: 64 additions & 27 deletions vlib/v/parser/parse_type.v
Expand Up @@ -25,38 +25,49 @@ fn (mut p Parser) parse_array_type(expecting token.Kind, is_option bool) ast.Typ
}
ast.Ident {
mut show_non_const_error := false
if mut const_field := p.table.global_scope.find_const('${p.mod}.${size_expr.name}') {
if mut const_field.expr is ast.IntegerLiteral {
fixed_size = const_field.expr.val.int()
} else {
if mut const_field.expr is ast.InfixExpr {
// QUESTION: this should most likely no be done in the parser, right?
mut t := transformer.new_transformer(p.pref)
folded_expr := t.infix_expr(mut const_field.expr)

if folded_expr is ast.IntegerLiteral {
fixed_size = folded_expr.val.int()
} else {
show_non_const_error = true
}
} else {
show_non_const_error = true
}
}
} else {
if p.pref.is_fmt {
// for vfmt purposes, pretend the constant does exist
// it may have been defined in another .v file:
fixed_size = 1
} else {
show_non_const_error = true
}
}
fixed_size, show_non_const_error = p.check_and_eval_const_val(size_expr)
if show_non_const_error {
p.error_with_pos('non-constant array bound `${size_expr.name}`',
size_expr.pos)
}
}
ast.InfixExpr {
mut show_non_const_error := false
mut left_val := 1
mut right_val := 1
if size_expr.left is ast.Ident {
left_val, show_non_const_error = p.check_and_eval_const_val(size_expr.left)
if show_non_const_error {
p.error_with_pos('non-constant array bound `${size_expr.left.name}`',
size_expr.left.pos)
}
}
if size_expr.right is ast.Ident && !show_non_const_error {
right_val, show_non_const_error = p.check_and_eval_const_val(size_expr.right)
if show_non_const_error {
p.error_with_pos('non-constant array bound `${size_expr.right.name}`',
size_expr.right.pos)
}
}
match size_expr.op {
.plus {
fixed_size = left_val + right_val
}
.minus {
fixed_size = left_val - right_val
}
.mul {
fixed_size = left_val * right_val
}
.div {
fixed_size = left_val / right_val
}
else {
p.error_with_pos('fixed array size cannot use non-constant value',
size_expr.pos)
}
}
}
else {
p.error_with_pos('fixed array size cannot use non-constant value',
size_expr.pos())
Expand Down Expand Up @@ -840,3 +851,29 @@ fn (mut p Parser) parse_generic_inst_type(name string) ast.Type {
}
return p.find_type_or_add_placeholder(name, .v).set_flag(.generic)
}

// return value and show_non_const_error
fn (mut p Parser) check_and_eval_const_val(expr ast.Ident) (int, bool) {
if mut const_field := p.table.global_scope.find_const('${p.mod}.${expr.name}') {
if mut const_field.expr is ast.IntegerLiteral {
return const_field.expr.val.int(), false
} else {
if mut const_field.expr is ast.InfixExpr {
// QUESTION: this should most likely no be done in the parser, right?
mut t := transformer.new_transformer(p.pref)
folded_expr := t.infix_expr(mut const_field.expr)

if folded_expr is ast.IntegerLiteral {
return folded_expr.val.int(), false
}
}
}
} else {
if p.pref.is_fmt {
// for vfmt purposes, pretend the constant does exist
// it may have been defined in another .v file:
return 1, false
}
}
return 0, true
}
15 changes: 15 additions & 0 deletions vlib/v/tests/fixed_array_const_size_test.v
Expand Up @@ -44,3 +44,18 @@ fn test_int_const_used_as_fixed_array_size() {
dump(bb.len)
assert aa.len == 10_000
}

const (
rows = 4
cols = 4
)

struct Matrix {
data [rows * cols]int
}

fn test_infix_const_expr_used_as_fixed_array_size() {
mat := Matrix{}
println(mat)
assert mat.data.len == 16
}

0 comments on commit e6b7eb3

Please sign in to comment.