From 667f65bb5fde05bcf1148784049cc28088efd4fa Mon Sep 17 00:00:00 2001 From: yuyi Date: Fri, 23 Feb 2024 01:22:13 +0800 Subject: [PATCH] checker: check assigning immutable reference struct field (fix #20814) (#20883) --- cmd/tools/vast/vast.v | 8 +++--- cmd/tools/vfmt.v | 14 ++++++---- cmd/tools/vtest-parser.v | 3 +- cmd/tools/vvet/vvet.v | 4 +-- doc/docs.md | 4 +-- vlib/v/ast/type_size_test.v | 4 +-- vlib/v/ast/walker/walker_test.v | 4 +-- vlib/v/builder/builder.v | 10 +++---- vlib/v/builder/cbuilder/cbuilder.v | 2 +- vlib/v/builder/golangbuilder/golangbuilder.v | 2 +- vlib/v/builder/jsbuilder/jsbuilder.v | 2 +- vlib/v/builder/nativebuilder/nativebuilder.v | 2 +- vlib/v/builder/wasmbuilder/wasmbuilder.v | 2 +- vlib/v/checker/assign.v | 26 +++++++++++++++-- ...n_immutable_reference_struct_field_err.out | 7 +++++ ...gn_immutable_reference_struct_field_err.vv | 19 +++++++++++++ vlib/v/doc/doc.v | 2 +- vlib/v/doc/module.v | 4 +-- vlib/v/fmt/fmt.v | 6 ++-- vlib/v/fmt/fmt_bin2v_test.v | 6 ++-- vlib/v/fmt/fmt_keep_test.v | 6 ++-- vlib/v/fmt/fmt_test.v | 6 ++-- vlib/v/fmt/fmt_vlib_test.v | 6 ++-- vlib/v/gen/c/cgen.v | 4 +-- vlib/v/gen/golang/golang.v | 6 ++-- vlib/v/gen/js/js.v | 2 +- vlib/v/gen/native/gen.v | 2 +- vlib/v/gen/wasm/gen.v | 2 +- vlib/v/markused/markused.v | 2 +- vlib/v/parser/comptime.v | 2 +- vlib/v/parser/parser.v | 17 +++++------ vlib/v/parser/v_parser_test.v | 28 +++++++++---------- vlib/v/tests/option_selector_assign_test.v | 2 +- vlib/v/tests/parse_invalid_map_type_test.v | 8 ++++-- 34 files changed, 138 insertions(+), 86 deletions(-) create mode 100644 vlib/v/checker/tests/assign_immutable_reference_struct_field_err.out create mode 100644 vlib/v/checker/tests/assign_immutable_reference_struct_field_err.vv diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 9ea22164f..f53747572 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -131,7 +131,7 @@ fn json(file string) string { pref: pref_ } // parse file with comment - ast_file := parser.parse_file(file, t.table, .parse_comments, t.pref) + ast_file := parser.parse_file(file, mut t.table, .parse_comments, t.pref) t.root = t.ast_file(ast_file) // generate the ast string s := json_print(t.root) @@ -140,10 +140,10 @@ fn json(file string) string { // the ast tree struct Tree { - table &ast.Table = unsafe { nil } - pref &pref.Preferences = unsafe { nil } + pref &pref.Preferences = unsafe { nil } mut: - root Node // the root of tree + table &ast.Table = unsafe { nil } + root Node // the root of tree } // tree node diff --git a/cmd/tools/vfmt.v b/cmd/tools/vfmt.v index 7b7d08b18..403b57648 100644 --- a/cmd/tools/vfmt.v +++ b/cmd/tools/vfmt.v @@ -160,10 +160,10 @@ fn (foptions &FormatOptions) vlog(msg string) { fn (foptions &FormatOptions) format_file(file string) { foptions.vlog('vfmt2 running fmt.fmt over file: ${file}') - prefs, table := setup_preferences_and_table() - file_ast := parser.parse_file(file, table, .parse_comments, prefs) + prefs, mut table := setup_preferences_and_table() + file_ast := parser.parse_file(file, mut table, .parse_comments, prefs) // checker.new_checker(table, prefs).check(file_ast) - formatted_content := fmt.fmt(file_ast, table, prefs, foptions.is_debug) + formatted_content := fmt.fmt(file_ast, mut table, prefs, foptions.is_debug) file_name := os.file_name(file) ulid := rand.ulid() vfmt_output_path := os.join_path(vtmp_folder, 'vfmt_${ulid}_${file_name}') @@ -174,11 +174,13 @@ fn (foptions &FormatOptions) format_file(file string) { fn (foptions &FormatOptions) format_pipe() { foptions.vlog('vfmt2 running fmt.fmt over stdin') - prefs, table := setup_preferences_and_table() + prefs, mut table := setup_preferences_and_table() input_text := os.get_raw_lines_joined() - file_ast := parser.parse_text(input_text, '', table, .parse_comments, prefs) + file_ast := parser.parse_text(input_text, '', mut table, .parse_comments, prefs) // checker.new_checker(table, prefs).check(file_ast) - formatted_content := fmt.fmt(file_ast, table, prefs, foptions.is_debug, source_text: input_text) + formatted_content := fmt.fmt(file_ast, mut table, prefs, foptions.is_debug, + source_text: input_text + ) print(formatted_content) flush_stdout() foptions.vlog('fmt.fmt worked and ${formatted_content.len} bytes were written to stdout.') diff --git a/cmd/tools/vtest-parser.v b/cmd/tools/vtest-parser.v index 79e711277..33e469895 100644 --- a/cmd/tools/vtest-parser.v +++ b/cmd/tools/vtest-parser.v @@ -56,7 +56,8 @@ fn main() { time.sleep(ms * time.millisecond) exit(ecode_timeout) }(context.timeout_ms) - _ := parser.parse_text(source, context.path, context.table, .skip_comments, context.pref) + _ := parser.parse_text(source, context.path, mut context.table, .skip_comments, + context.pref) context.log('> worker ${pid:5} finished parsing ${context.path}') exit(0) } else { diff --git a/cmd/tools/vvet/vvet.v b/cmd/tools/vvet/vvet.v index 3a39c78c1..5a3fefdcc 100644 --- a/cmd/tools/vvet/vvet.v +++ b/cmd/tools/vvet/vvet.v @@ -103,9 +103,9 @@ fn (mut vt Vet) vet_file(path string) { mut prefs := pref.new_preferences() prefs.is_vet = true prefs.is_vsh = path.ends_with('.vsh') - table := ast.new_table() + mut table := ast.new_table() vt.vprintln("vetting file '${path}'...") - _, errors, notices := parser.parse_vet_file(path, table, prefs) + _, errors, notices := parser.parse_vet_file(path, mut table, prefs) // Transfer errors from scanner and parser vt.errors << errors vt.notices << notices diff --git a/doc/docs.md b/doc/docs.md index 52adbdbb9..d046d6130 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -4943,7 +4943,7 @@ struct MyStruct { } fn main() { - m := MyStruct{} + mut m := MyStruct{} mut r := RefStruct{ r: &m } @@ -5046,7 +5046,7 @@ fn use_stack() { } fn main() { - m := MyStruct{} + mut m := MyStruct{} mut r := RefStruct{ r: &m } diff --git a/vlib/v/ast/type_size_test.v b/vlib/v/ast/type_size_test.v index a92663471..63e33c8ad 100644 --- a/vlib/v/ast/type_size_test.v +++ b/vlib/v/ast/type_size_test.v @@ -35,9 +35,9 @@ fn test_type_size() { mut b := builder.new_builder(pref_) mut files := b.get_builtin_files() b.set_module_lookup_paths() - parser.parse_files(files, b.table, b.pref) + parser.parse_files(files, mut b.table, b.pref) b.parse_imports() - parser.parse_file(@FILE, b.table, .parse_comments, b.pref) + parser.parse_file(@FILE, mut b.table, .parse_comments, b.pref) mut t := b.table diff --git a/vlib/v/ast/walker/walker_test.v b/vlib/v/ast/walker/walker_test.v index 4a370cacc..3e0d71825 100644 --- a/vlib/v/ast/walker/walker_test.v +++ b/vlib/v/ast/walker/walker_test.v @@ -4,9 +4,9 @@ import v.parser import v.pref fn parse_text(text string) &ast.File { - tbl := ast.new_table() + mut tbl := ast.new_table() prefs := pref.new_preferences() - return parser.parse_text(text, '', tbl, .skip_comments, prefs) + return parser.parse_text(text, '', mut tbl, .skip_comments, prefs) } struct NodeByOffset { diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index 818f5f8d9..bd98be780 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -89,8 +89,8 @@ pub fn new_builder(pref_ &pref.Preferences) Builder { } pub fn (mut b Builder) interpret_text(code string, v_files []string) ! { - b.parsed_files = parser.parse_files(v_files, b.table, b.pref) - b.parsed_files << parser.parse_text(code, '', b.table, .skip_comments, b.pref) + b.parsed_files = parser.parse_files(v_files, mut b.table, b.pref) + b.parsed_files << parser.parse_text(code, '', mut b.table, .skip_comments, b.pref) b.parse_imports() if b.pref.only_check_syntax { @@ -109,7 +109,7 @@ pub fn (mut b Builder) front_stages(v_files []string) ! { util.timing_start('PARSE') util.timing_start('Builder.front_stages.parse_files') - b.parsed_files = parser.parse_files(v_files, b.table, b.pref) + b.parsed_files = parser.parse_files(v_files, mut b.table, b.pref) timers.show('Builder.front_stages.parse_files') b.parse_imports() @@ -144,7 +144,7 @@ pub fn (mut b Builder) middle_stages() ! { // b.table.complete_interface_check() if b.pref.skip_unused { - markused.mark_used(mut b.table, b.pref, b.parsed_files) + markused.mark_used(mut b.table, mut b.pref, b.parsed_files) } if b.pref.show_callgraph { callgraph.show(mut b.table, b.pref, b.parsed_files) @@ -222,7 +222,7 @@ pub fn (mut b Builder) parse_imports() { } // eprintln('>> ast_file.path: $ast_file.path , done: $done_imports, `import $mod` => $v_files') // Add all imports referenced by these libs - parsed_files := parser.parse_files(v_files, b.table, b.pref) + parsed_files := parser.parse_files(v_files, mut b.table, b.pref) for file in parsed_files { mut name := file.mod.name if name == '' { diff --git a/vlib/v/builder/cbuilder/cbuilder.v b/vlib/v/builder/cbuilder/cbuilder.v index 3667f66cd..585dbff85 100644 --- a/vlib/v/builder/cbuilder/cbuilder.v +++ b/vlib/v/builder/cbuilder/cbuilder.v @@ -73,7 +73,7 @@ pub fn gen_c(mut b builder.Builder, v_files []string) string { } util.timing_start('C GEN') - header, res, out_str, out_fn_start_pos := c.gen(b.parsed_files, b.table, b.pref) + header, res, out_str, out_fn_start_pos := c.gen(b.parsed_files, mut b.table, b.pref) util.timing_measure('C GEN') if b.pref.parallel_cc { diff --git a/vlib/v/builder/golangbuilder/golangbuilder.v b/vlib/v/builder/golangbuilder/golangbuilder.v index 79cae393e..a6ab0c7b9 100644 --- a/vlib/v/builder/golangbuilder/golangbuilder.v +++ b/vlib/v/builder/golangbuilder/golangbuilder.v @@ -36,6 +36,6 @@ pub fn build_golang(mut b builder.Builder, v_files []string, out_file string) { } b.front_and_middle_stages(nvf) or { return } util.timing_start('Golang GEN') - b.stats_lines, b.stats_bytes = golang.gen(b.parsed_files, b.table, out_file, b.pref) + b.stats_lines, b.stats_bytes = golang.gen(b.parsed_files, mut b.table, out_file, b.pref) util.timing_measure('Golang GEN') } diff --git a/vlib/v/builder/jsbuilder/jsbuilder.v b/vlib/v/builder/jsbuilder/jsbuilder.v index 2b3941750..c37148d05 100644 --- a/vlib/v/builder/jsbuilder/jsbuilder.v +++ b/vlib/v/builder/jsbuilder/jsbuilder.v @@ -41,7 +41,7 @@ pub fn build_js(mut b builder.Builder, v_files []string, out_file string) { pub fn gen_js(mut b builder.Builder, v_files []string) string { b.front_and_middle_stages(v_files) or { return '' } util.timing_start('JS GEN') - res := js.gen(b.parsed_files, b.table, b.pref) + res := js.gen(b.parsed_files, mut b.table, b.pref) util.timing_measure('JS GEN') return res } diff --git a/vlib/v/builder/nativebuilder/nativebuilder.v b/vlib/v/builder/nativebuilder/nativebuilder.v index 790eea454..03c78da8a 100644 --- a/vlib/v/builder/nativebuilder/nativebuilder.v +++ b/vlib/v/builder/nativebuilder/nativebuilder.v @@ -58,6 +58,6 @@ pub fn build_native(mut b builder.Builder, v_files []string, out_file string) { eprintln('Error: Only arm64 and amd64 are supported by V') } } - b.stats_lines, b.stats_bytes = native.gen(b.parsed_files, b.table, out_file, b.pref) + b.stats_lines, b.stats_bytes = native.gen(b.parsed_files, mut b.table, out_file, b.pref) util.timing_measure('Native GEN') } diff --git a/vlib/v/builder/wasmbuilder/wasmbuilder.v b/vlib/v/builder/wasmbuilder/wasmbuilder.v index 95675a4a5..8518e43c4 100644 --- a/vlib/v/builder/wasmbuilder/wasmbuilder.v +++ b/vlib/v/builder/wasmbuilder/wasmbuilder.v @@ -31,6 +31,6 @@ pub fn compile_wasm(mut b builder.Builder) { pub fn build_wasm(mut b builder.Builder, v_files []string, out_file string) { b.front_and_middle_stages(v_files) or { return } util.timing_start('WebAssembly GEN') - wasm.gen(b.parsed_files, b.table, out_file, b.pref) + wasm.gen(b.parsed_files, mut b.table, out_file, b.pref) util.timing_measure('WebAssembly GEN') } diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index f7aea8ca8..43311fe4c 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -223,11 +223,31 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { } } } - if mut left is ast.Ident && mut right is ast.Ident { - if !c.inside_unsafe && left_type.is_ptr() && left.is_mut() && right_type.is_ptr() - && !right.is_mut() { + if left is ast.Ident && left.is_mut() && !c.inside_unsafe { + if left_type.is_ptr() && mut right is ast.Ident && !right.is_mut() + && right_type.is_ptr() { c.error('`${right.name}` is immutable, cannot have a mutable reference to an immutable object', right.pos) + } else if mut right is ast.StructInit { + typ_sym := c.table.sym(right.typ) + for init_field in right.init_fields { + if field_info := c.table.find_field_with_embeds(typ_sym, init_field.name) { + if field_info.is_mut { + if init_field.expr is ast.Ident && !init_field.expr.is_mut() + && init_field.typ.is_ptr() { + c.note('`${init_field.expr.name}` is immutable, cannot have a mutable reference to an immutable object', + init_field.pos) + } else if init_field.expr is ast.PrefixExpr { + if init_field.expr.op == .amp + && init_field.expr.right is ast.Ident + && !init_field.expr.right.is_mut() { + c.note('`${init_field.expr.right.name}` is immutable, cannot have a mutable reference to an immutable object', + init_field.expr.right.pos) + } + } + } + } + } } } } else { diff --git a/vlib/v/checker/tests/assign_immutable_reference_struct_field_err.out b/vlib/v/checker/tests/assign_immutable_reference_struct_field_err.out new file mode 100644 index 000000000..5c86a9172 --- /dev/null +++ b/vlib/v/checker/tests/assign_immutable_reference_struct_field_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_immutable_reference_struct_field_err.vv:13:29: notice: `parent` is immutable, cannot have a mutable reference to an immutable object + 11 | } + 12 | // taking a reference of `parent` and putting it under a `mut:` struct field + 13 | mut child := Tree{parent: &parent} + | ~~~~~~ + 14 | + 15 | // unwrap the reference of `parent`, but declare it as `mut` diff --git a/vlib/v/checker/tests/assign_immutable_reference_struct_field_err.vv b/vlib/v/checker/tests/assign_immutable_reference_struct_field_err.vv new file mode 100644 index 000000000..7941226d9 --- /dev/null +++ b/vlib/v/checker/tests/assign_immutable_reference_struct_field_err.vv @@ -0,0 +1,19 @@ +struct Tree { +mut: + garbage int + parent ?&Tree +} + +fn main() { + // `parent` is not declared as mutable! + parent := Tree{ + garbage: 11 + } + // taking a reference of `parent` and putting it under a `mut:` struct field + mut child := Tree{parent: &parent} + + // unwrap the reference of `parent`, but declare it as `mut` + mut inner_parent := child.parent? + inner_parent.garbage = 777 // !we can mutate `parent` freely! + println(parent.garbage) // 777 +} diff --git a/vlib/v/doc/doc.v b/vlib/v/doc/doc.v index e76873f9c..0f0332ae4 100644 --- a/vlib/v/doc/doc.v +++ b/vlib/v/doc/doc.v @@ -453,7 +453,7 @@ pub fn (mut d Doc) generate() ! { if i == 0 { d.parent_mod_name = get_parent_mod(d.base_path) or { '' } } - file_asts << parser.parse_file(file_path, d.table, comments_mode, d.prefs) + file_asts << parser.parse_file(file_path, mut d.table, comments_mode, d.prefs) } return d.file_asts(mut file_asts) } diff --git a/vlib/v/doc/module.v b/vlib/v/doc/module.v index 72176a0d9..cb41c097d 100644 --- a/vlib/v/doc/module.v +++ b/vlib/v/doc/module.v @@ -42,8 +42,8 @@ fn get_parent_mod(input_dir string) !string { } return error('No V files found.') } - tbl := ast.new_table() - file_ast := parser.parse_file(v_files[0], tbl, .skip_comments, prefs) + mut tbl := ast.new_table() + file_ast := parser.parse_file(v_files[0], mut tbl, .skip_comments, prefs) if file_ast.mod.name == 'main' { return '' } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 8c487398e..18140524c 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -16,10 +16,10 @@ const max_len = [0, 35, 60, 85, 93, 100] @[minify] pub struct Fmt { + pref &pref.Preferences = unsafe { nil } pub mut: file ast.File - table &ast.Table = unsafe { nil } - pref &pref.Preferences = unsafe { nil } + table &ast.Table = unsafe { nil } is_debug bool out strings.Builder out_imports strings.Builder @@ -62,7 +62,7 @@ pub struct FmtOptions { source_text string } -pub fn fmt(file ast.File, table &ast.Table, pref_ &pref.Preferences, is_debug bool, options FmtOptions) string { +pub fn fmt(file ast.File, mut table ast.Table, pref_ &pref.Preferences, is_debug bool, options FmtOptions) string { mut f := Fmt{ file: file table: table diff --git a/vlib/v/fmt/fmt_bin2v_test.v b/vlib/v/fmt/fmt_bin2v_test.v index de47d01c7..f36ace474 100644 --- a/vlib/v/fmt/fmt_bin2v_test.v +++ b/vlib/v/fmt/fmt_bin2v_test.v @@ -26,9 +26,9 @@ fn test_bin2v_formatting() { } prepare_bin2v_file() - table := ast.new_table() - file_ast := parser.parse_file(b2v_keep_path, table, .parse_comments, fpref) - result_ocontent := fmt.fmt(file_ast, table, fpref, false) + mut table := ast.new_table() + file_ast := parser.parse_file(b2v_keep_path, mut table, .parse_comments, fpref) + result_ocontent := fmt.fmt(file_ast, mut table, fpref, false) eprintln('> the file ${b2v_keep_path} can be formatted.') expected_ocontent := os.read_file(b2v_keep_path)! if expected_ocontent != result_ocontent { diff --git a/vlib/v/fmt/fmt_keep_test.v b/vlib/v/fmt/fmt_keep_test.v index 33c1d22ec..a82875e1e 100644 --- a/vlib/v/fmt/fmt_keep_test.v +++ b/vlib/v/fmt/fmt_keep_test.v @@ -50,9 +50,9 @@ fn test_fmt() { eprintln(fmt_bench.step_message_fail('cannot read from ${vrelpath}')) continue } - table := ast.new_table() - file_ast := parser.parse_file(ipath, table, .parse_comments, fpref) - result_ocontent := fmt.fmt(file_ast, table, fpref, false) + mut table := ast.new_table() + file_ast := parser.parse_file(ipath, mut table, .parse_comments, fpref) + result_ocontent := fmt.fmt(file_ast, mut table, fpref, false) if expected_ocontent != result_ocontent { fmt_bench.fail() eprintln(fmt_bench.step_message_fail('file ${vrelpath} after formatting, does not look as expected.')) diff --git a/vlib/v/fmt/fmt_test.v b/vlib/v/fmt/fmt_test.v index 214192034..dfb4a33f0 100644 --- a/vlib/v/fmt/fmt_test.v +++ b/vlib/v/fmt/fmt_test.v @@ -46,9 +46,9 @@ fn test_fmt() { eprintln(fmt_bench.step_message_fail('cannot read from ${opath}')) continue } - table := ast.new_table() - file_ast := parser.parse_file(ipath, table, .parse_comments, fpref) - result_ocontent := fmt.fmt(file_ast, table, fpref, false) + mut table := ast.new_table() + file_ast := parser.parse_file(ipath, mut table, .parse_comments, fpref) + result_ocontent := fmt.fmt(file_ast, mut table, fpref, false) if expected_ocontent != result_ocontent { fmt_bench.fail() eprintln(fmt_bench.step_message_fail('file ${ipath} after formatting, does not look as expected.')) diff --git a/vlib/v/fmt/fmt_vlib_test.v b/vlib/v/fmt/fmt_vlib_test.v index 637ad91e6..341510ed1 100644 --- a/vlib/v/fmt/fmt_vlib_test.v +++ b/vlib/v/fmt/fmt_vlib_test.v @@ -44,9 +44,9 @@ fn test_vlib_fmt() { eprintln(fmt_bench.step_message_fail('cannot read from ${opath}')) continue } - table := ast.new_table() - file_ast := parser.parse_file(ipath, table, .parse_comments, fpref) - result_ocontent := fmt.fmt(file_ast, table, fpref, false) + mut table := ast.new_table() + file_ast := parser.parse_file(ipath, mut table, .parse_comments, fpref) + result_ocontent := fmt.fmt(file_ast, mut table, fpref, false) if expected_ocontent != result_ocontent { fmt_bench.fail() eprintln(fmt_bench.step_message_fail('file ${ipath} after formatting, does not look as expected.')) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index c64b424a2..f2b13da2a 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -261,7 +261,7 @@ struct GlobalConstDef { is_precomputed bool // can be declared as a const in C: primitive, and a simple definition } -pub fn gen(files []&ast.File, table &ast.Table, pref_ &pref.Preferences) (string, string, string, []int) { +pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) (string, string, string, []int) { mut module_built := '' if pref_.build_mode == .build_module { for file in files { @@ -313,7 +313,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref_ &pref.Preferences) (string module_built: module_built timers_should_print: timers_should_print timers: util.new_timers(should_print: timers_should_print, label: 'global_cgen') - inner_loop: &ast.empty_stmt + inner_loop: unsafe { &ast.empty_stmt } field_data_type: ast.Type(table.find_type_idx('FieldData')) enum_data_type: ast.Type(table.find_type_idx('EnumData')) is_cc_msvc: pref_.ccompiler == 'msvc' diff --git a/vlib/v/gen/golang/golang.v b/vlib/v/gen/golang/golang.v index a163a14e0..5c5c68c3e 100644 --- a/vlib/v/gen/golang/golang.v +++ b/vlib/v/gen/golang/golang.v @@ -12,9 +12,9 @@ import os const bs = '\\' pub struct Gen { + pref &pref.Preferences = unsafe { nil } pub mut: - table &ast.Table = unsafe { nil } - pref &pref.Preferences = unsafe { nil } + table &ast.Table = unsafe { nil } // is_debug bool out strings.Builder out_imports strings.Builder @@ -45,7 +45,7 @@ pub mut: nlines int } -pub fn gen(files []&ast.File, table &ast.Table, out_file string, pref_ &pref.Preferences) (int, int) { +pub fn gen(files []&ast.File, mut table ast.Table, out_file string, pref_ &pref.Preferences) (int, int) { mut g := Gen{ table: table pref: pref_ diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index d0db5cfc0..a15e4dacf 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -97,7 +97,7 @@ fn (mut g JsGen) write_tests_definitions() { g.definitions.writeln('globalThis.g_test_fails = 0;') } -pub fn gen(files []&ast.File, table &ast.Table, pref_ &pref.Preferences) string { +pub fn gen(files []&ast.File, mut table ast.Table, pref_ &pref.Preferences) string { mut g := &JsGen{ definitions: strings.new_builder(100) table: table diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 593093098..f27e72196 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -327,7 +327,7 @@ fn get_backend(arch pref.Arch, target_os pref.OS) !CodeGen { return error('unsupported architecture') } -pub fn gen(files []&ast.File, table &ast.Table, out_name string, pref_ &pref.Preferences) (int, int) { +pub fn gen(files []&ast.File, mut table ast.Table, out_name string, pref_ &pref.Preferences) (int, int) { exe_name := if pref_.os == .windows && !out_name.ends_with('.exe') { out_name + '.exe' } else { diff --git a/vlib/v/gen/wasm/gen.v b/vlib/v/gen/wasm/gen.v index f4c825ae7..04e1d6fd1 100644 --- a/vlib/v/gen/wasm/gen.v +++ b/vlib/v/gen/wasm/gen.v @@ -1283,7 +1283,7 @@ pub fn (mut g Gen) calculate_enum_fields() { } } -pub fn gen(files []&ast.File, table &ast.Table, out_name string, w_pref &pref.Preferences) { +pub fn gen(files []&ast.File, mut table ast.Table, out_name string, w_pref &pref.Preferences) { stack_top := w_pref.wasm_stack_top mut g := &Gen{ table: table diff --git a/vlib/v/markused/markused.v b/vlib/v/markused/markused.v index 831db7cad..8e53bae61 100644 --- a/vlib/v/markused/markused.v +++ b/vlib/v/markused/markused.v @@ -7,7 +7,7 @@ import v.util import v.pref // mark_used walks the AST, starting at main() and marks all used fns transitively -pub fn mark_used(mut table ast.Table, pref_ &pref.Preferences, ast_files []&ast.File) { +pub fn mark_used(mut table ast.Table, mut pref_ pref.Preferences, ast_files []&ast.File) { mut all_fns, all_consts, all_globals := all_fn_const_and_global(ast_files) util.timing_start(@METHOD) defer { diff --git a/vlib/v/parser/comptime.v b/vlib/v/parser/comptime.v index aeea8dc79..2bf4f0d56 100644 --- a/vlib/v/parser/comptime.v +++ b/vlib/v/parser/comptime.v @@ -282,7 +282,7 @@ fn (mut p Parser) comptime_call() ast.ComptimeCall { // the tmpl inherits all parent scopes. previous functionality was just to // inherit the scope from which the comptime call was made and no parents. // this is much simpler and allows access to globals. can be changed if needed. - mut file := parse_comptime(tmpl_path, v_code, p.table, p.pref, p.scope) + mut file := parse_comptime(tmpl_path, v_code, mut p.table, p.pref, mut p.scope) file.path = tmpl_path return ast.ComptimeCall{ scope: unsafe { nil } diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 737d2b534..bc0393fc1 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -116,7 +116,7 @@ pub mut: __global codegen_files = unsafe { []&ast.File{} } // for tests -pub fn parse_stmt(text string, table &ast.Table, scope &ast.Scope) ast.Stmt { +pub fn parse_stmt(text string, mut table ast.Table, mut scope ast.Scope) ast.Stmt { $if trace_parse_stmt ? { eprintln('> ${@MOD}.${@FN} text: ${text}') } @@ -136,7 +136,7 @@ pub fn parse_stmt(text string, table &ast.Table, scope &ast.Scope) ast.Stmt { return p.stmt(false) } -pub fn parse_comptime(tmpl_path string, text string, table &ast.Table, pref_ &pref.Preferences, scope &ast.Scope) &ast.File { +pub fn parse_comptime(tmpl_path string, text string, mut table ast.Table, pref_ &pref.Preferences, mut scope ast.Scope) &ast.File { $if trace_parse_comptime ? { eprintln('> ${@MOD}.${@FN} text: ${text}') } @@ -154,7 +154,7 @@ pub fn parse_comptime(tmpl_path string, text string, table &ast.Table, pref_ &pr return res } -pub fn parse_text(text string, path string, table &ast.Table, comments_mode scanner.CommentsMode, pref_ &pref.Preferences) &ast.File { +pub fn parse_text(text string, path string, mut table ast.Table, comments_mode scanner.CommentsMode, pref_ &pref.Preferences) &ast.File { $if trace_parse_text ? { eprintln('> ${@MOD}.${@FN} comments_mode: ${comments_mode:-20} | path: ${path:-20} | text: ${text}') } @@ -232,7 +232,7 @@ pub fn (mut p Parser) set_path(path string) { } } -pub fn parse_file(path string, table &ast.Table, comments_mode scanner.CommentsMode, pref_ &pref.Preferences) &ast.File { +pub fn parse_file(path string, mut table ast.Table, comments_mode scanner.CommentsMode, pref_ &pref.Preferences) &ast.File { // Note: when comments_mode == .toplevel_comments, // the parser gives feedback to the scanner about toplevel statements, so that the scanner can skip // all the tricky inner comments. This is needed because we do not have a good general solution @@ -258,7 +258,7 @@ pub fn parse_file(path string, table &ast.Table, comments_mode scanner.CommentsM return res } -pub fn parse_vet_file(path string, table_ &ast.Table, pref_ &pref.Preferences) (&ast.File, []vet.Error, []vet.Error) { +pub fn parse_vet_file(path string, mut table_ ast.Table, pref_ &pref.Preferences) (&ast.File, []vet.Error, []vet.Error) { $if trace_parse_vet_file ? { eprintln('> ${@MOD}.${@FN} path: ${path}') } @@ -367,7 +367,8 @@ pub fn (mut p Parser) parse() &ast.File { // codegen if p.codegen_text.len > 0 && !p.pref.is_fmt { ptext := 'module ' + p.mod.all_after_last('.') + '\n' + p.codegen_text - codegen_files << parse_text(ptext, p.file_name, p.table, p.comments_mode, p.pref) + codegen_files << parse_text(ptext, p.file_name, mut p.table, p.comments_mode, + p.pref) } return &ast.File{ @@ -427,7 +428,7 @@ fn (mut q Queue) run() { } } */ -pub fn parse_files(paths []string, table &ast.Table, pref_ &pref.Preferences) []&ast.File { +pub fn parse_files(paths []string, mut table ast.Table, pref_ &pref.Preferences) []&ast.File { mut timers := util.new_timers(should_print: false, label: 'parse_files: ${paths}') $if time_parsing ? { timers.should_print = true @@ -459,7 +460,7 @@ pub fn parse_files(paths []string, table &ast.Table, pref_ &pref.Preferences) [] mut files := []&ast.File{cap: paths.len} for path in paths { timers.start('parse_file ${path}') - files << parse_file(path, table, .skip_comments, pref_) + files << parse_file(path, mut table, .skip_comments, pref_) timers.show('parse_file ${path}') } if codegen_files.len > 0 { diff --git a/vlib/v/parser/v_parser_test.v b/vlib/v/parser/v_parser_test.v index d35a58702..e9b4653e8 100644 --- a/vlib/v/parser/v_parser_test.v +++ b/vlib/v/parser/v_parser_test.v @@ -79,9 +79,9 @@ fn main() { ff(8+x) } ' - table := ast.new_table() + mut table := ast.new_table() vpref := &pref.Preferences{} - mut prog := parse_text(source_text, '', table, .skip_comments, vpref) + mut prog := parse_text(source_text, '', mut table, .skip_comments, vpref) mut checker_ := checker.new_checker(table, vpref) checker_.check(mut prog) } @@ -93,14 +93,14 @@ fn test_one() { println(@LOCATION) input := ['a := 10', 'b := -a', 'c := 20'] expected := 'int a = 10;int b = -a;int c = 20;' - table := ast.new_table() + mut table := ast.new_table() vpref := &pref.Preferences{} - scope := &ast.Scope{ + mut scope := &ast.Scope{ start_pos: 0 } mut e := []ast.Stmt{} for line in input { - e << parse_stmt(line, table, scope) + e << parse_stmt(line, mut table, mut scope) } mut program := &ast.File{ stmts: e @@ -109,7 +109,7 @@ fn test_one() { } mut checker_ := checker.new_checker(table, vpref) checker_.check(mut program) - mut res, _, _, _ := c.gen([program], table, vpref) + mut res, _, _, _ := c.gen([program], mut table, vpref) res = res.replace('\n', '').trim_space().after('#endif') println(res) ok := expected == res @@ -136,15 +136,15 @@ fn test_parse_expr() { 'string s = tos3("hi");', 'x = 11;', 'a += 10;', '1.2 + 3.4;', '4 + 4;', '1 + 2 * 5;', '-a + 1;', '2 + 2;'] mut e := []ast.Stmt{} - table := ast.new_table() + mut table := ast.new_table() vpref := &pref.Preferences{} mut chk := checker.new_checker(table, vpref) - scope := &ast.Scope{ + mut scope := &ast.Scope{ start_pos: 0 } for s in input { println('\n\nst="${s}"') - e << parse_stmt(s, table, scope) + e << parse_stmt(s, mut table, mut scope) } mut program := &ast.File{ stmts: e @@ -152,7 +152,7 @@ fn test_parse_expr() { global_scope: scope } chk.check(mut program) - mut res, _, _, _ := c.gen([program], table, vpref) + mut res, _, _, _ := c.gen([program], mut table, vpref) res = res.after('#endif') println('========') println(res) @@ -185,13 +185,13 @@ fn test_num_literals() { 'c := -12.', 'd := -a', ] - table := ast.new_table() + mut table := ast.new_table() mut scope := &ast.Scope{ start_pos: 0 } mut rhs_types := []string{} for input in inputs { - stmt := parse_stmt(input, table, scope) + stmt := parse_stmt(input, mut table, mut scope) r := (stmt as ast.AssignStmt).right match r[0] { ast.IntegerLiteral { rhs_types << 'int literal' } @@ -297,8 +297,8 @@ fn parse(output_mode pref.OutputMode) ! { pref_.output_mode = output_mode for idx, f in files { // eprintln('> parsing in mode: ${output_mode}, ${idx+1:5}/${files.len} $f ...') - table := ast.new_table() - p := parse_file(f, table, .parse_comments, pref_) + mut table := ast.new_table() + p := parse_file(f, mut table, .parse_comments, pref_) assert !isnil(p), 'failed to parse `${f}` in mode: ${output_mode}' assert p.errors.len == 0, 'file ${f} should have been parsed with 0 errors' } diff --git a/vlib/v/tests/option_selector_assign_test.v b/vlib/v/tests/option_selector_assign_test.v index 08d45dad5..bc0862295 100644 --- a/vlib/v/tests/option_selector_assign_test.v +++ b/vlib/v/tests/option_selector_assign_test.v @@ -13,7 +13,7 @@ fn test_main() { nr_elems: 11 } mut child := Tree{ - parent: &parent + parent: unsafe { &parent } } child.set_nr_elems('Buzz', 123) assert child.parent or { return }.nr_elems == 123 diff --git a/vlib/v/tests/parse_invalid_map_type_test.v b/vlib/v/tests/parse_invalid_map_type_test.v index 1a562db02..c47597317 100644 --- a/vlib/v/tests/parse_invalid_map_type_test.v +++ b/vlib/v/tests/parse_invalid_map_type_test.v @@ -3,11 +3,13 @@ import v.parser import v.pref fn test_parser_map_type() { - result := parser.parse_text('a := map[*Node]bool', '', ast.new_table(), .parse_comments, - &pref.Preferences{ + mut table := ast.new_table() + pref_ := pref.Preferences{ output_mode: .silent is_fmt: true - }) + } + result := parser.parse_text('a := map[*Node]bool', '', mut table, .parse_comments, + pref_) println(result) assert true } -- 2.39.5