Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parser, checker: fix const var below at struct fixed array fields(fix#19593) #19893

Merged
merged 2 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions vlib/v/checker/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module checker

import v.ast
import v.util
import v.transformer

fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
util.timing_start(@METHOD)
Expand Down Expand Up @@ -52,6 +53,67 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
}
}

// Evaluate the size of the unresolved fixed array
for mut field in node.fields {
sym := c.table.sym(field.typ)
if sym.kind == .array_fixed {
info := sym.info as ast.ArrayFixed
if info.size > 0 {
continue
}
mut fixed_size := 0
match info.size_expr {
ast.Ident {
if mut const_field := c.table.global_scope.find_const('${c.mod}.${info.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 {
mut t := transformer.new_transformer_with_table(c.table,
c.pref)
folded_expr := t.infix_expr(mut const_field.expr)
if folded_expr is ast.IntegerLiteral {
fixed_size = folded_expr.val.int()
}
}
}
if fixed_size <= 0 {
c.error('non-constant array bound `${info.size_expr.name}`',
info.size_expr.pos)
}
}
ast.InfixExpr {
mut t := transformer.new_transformer_with_table(c.table, c.pref)
mut size_expr := unsafe { &info.size_expr }
folded_expr := t.infix_expr(mut size_expr)

if folded_expr is ast.IntegerLiteral {
fixed_size = folded_expr.val.int()
}
if fixed_size <= 0 {
c.error('fixed array size cannot use non-constant eval value',
info.size_expr.pos)
}
}
else {}
}
if fixed_size <= 0 {
c.error('fixed size cannot be zero or negative', info.size_expr.pos())
}
idx := c.table.find_or_register_array_fixed(info.elem_type, fixed_size,
info.size_expr, false)
if info.elem_type.has_flag(.generic) {
field.typ = ast.new_type(idx).set_flag(.generic)
} else {
field.typ = ast.new_type(idx)
}
for mut symfield in struct_sym.info.fields {
if symfield.name == field.name {
symfield.typ = field.typ
}
}
}
}

// Update .default_expr_typ for all fields in the struct:
util.timing_start('Checker.struct setting default_expr_typ')
old_expected_type := c.expected_type
Expand Down
4 changes: 2 additions & 2 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -1459,7 +1459,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 if !info.is_fn_ret {
} else if !info.is_fn_ret && len.int() > 0 {
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 {
Expand Down Expand Up @@ -6044,7 +6044,7 @@ fn (mut g Gen) write_types(symbols []&ast.TypeSymbol) {
mut def_str := 'typedef ${fixed_elem_name};'
def_str = def_str.replace_once('(*)', '(*${styp}[${len}])')
g.type_definitions.writeln(def_str)
} else {
} else if len > 0 {
g.type_definitions.writeln('typedef ${fixed_elem_name} ${styp} [${len}];')
}
}
Expand Down
40 changes: 16 additions & 24 deletions vlib/v/parser/parse_type.v
Original file line number Diff line number Diff line change
Expand Up @@ -16,57 +16,46 @@ fn (mut p Parser) parse_array_type(expecting token.Kind, is_option bool) ast.Typ
if p.tok.kind in [.number, .name] {
mut fixed_size := 0
mut size_expr := p.expr(0)
mut size_unresolved := true
if p.pref.is_fmt {
fixed_size = 987654321
} else {
match mut size_expr {
ast.IntegerLiteral {
fixed_size = size_expr.val.int()
size_unresolved = false
}
ast.Ident {
mut show_non_const_error := true
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()
show_non_const_error = 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_with_table(p.table,
p.pref)
folded_expr := t.infix_expr(mut const_field.expr)
size_unresolved = false
} else if mut const_field.expr is ast.InfixExpr {
// QUESTION: this should most likely no be done in the parser, right?
spytheman marked this conversation as resolved.
Show resolved Hide resolved
mut t := transformer.new_transformer_with_table(p.table, p.pref)
folded_expr := t.infix_expr(mut const_field.expr)

if folded_expr is ast.IntegerLiteral {
fixed_size = folded_expr.val.int()
show_non_const_error = false
}
if folded_expr is ast.IntegerLiteral {
fixed_size = folded_expr.val.int()
size_unresolved = false
}
}
} 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
show_non_const_error = false
size_unresolved = false
}
}
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 := true
mut t := transformer.new_transformer_with_table(p.table, p.pref)
folded_expr := t.infix_expr(mut size_expr)

if folded_expr is ast.IntegerLiteral {
fixed_size = folded_expr.val.int()
show_non_const_error = false
}
if show_non_const_error {
p.error_with_pos('fixed array size cannot use non-constant eval value',
size_expr.pos)
size_unresolved = false
}
}
else {
Expand All @@ -85,7 +74,10 @@ fn (mut p Parser) parse_array_type(expecting token.Kind, is_option bool) ast.Typ
// error is handled by parse_type
return 0
}
if fixed_size <= 0 {
// TODO:
// For now, when a const variable or expression is temporarily unavailable to evaluate,
// only pending struct fields are deferred.
if fixed_size <= 0 && (!p.inside_struct_field_decl || !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,
Expand Down
12 changes: 12 additions & 0 deletions vlib/v/tests/constant_array_size_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,15 @@ fn test_consant_array_size() {
mut b := [c_b_s]int{}
b = [1, 2]!
}

// test const was declared below struct fixed array fields declaration
struct Foo {
posts [max_posts_count]int
}

const max_posts_count = 5

fn test_const_below_at_struct_fixed_array_fields() {
foo := Foo{}
assert foo.posts == [0, 0, 0, 0, 0]!
}
Loading