From 4dff383e357fabb3fb8486ca9fb7caf06d3f065c Mon Sep 17 00:00:00 2001 From: shove Date: Thu, 16 Nov 2023 14:20:28 +0800 Subject: [PATCH] parser, checker: fix const var below at struct fixed array fields(fix#19593) (#19893) --- vlib/v/checker/struct.v | 62 +++++++++++++++++++++++++ vlib/v/gen/c/cgen.v | 4 +- vlib/v/parser/parse_type.v | 40 +++++++--------- vlib/v/tests/constant_array_size_test.v | 12 +++++ 4 files changed, 92 insertions(+), 26 deletions(-) diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 86065137ae..bbb2fe6ca2 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -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) @@ -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 diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 0f1edc6eac..75c15dc0a5 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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 { @@ -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}];') } } diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 3fc146beaf..1514323109 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -16,30 +16,28 @@ 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? + 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 { @@ -47,26 +45,17 @@ fn (mut p Parser) parse_array_type(expecting token.Kind, is_option bool) ast.Typ // 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 { @@ -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, diff --git a/vlib/v/tests/constant_array_size_test.v b/vlib/v/tests/constant_array_size_test.v index 9b575ba1e7..6a372808d9 100644 --- a/vlib/v/tests/constant_array_size_test.v +++ b/vlib/v/tests/constant_array_size_test.v @@ -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]! +}