From 3d83934caf66275531a232e6ac2a2419d74c4134 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Mon, 1 Jun 2020 15:43:54 +0200 Subject: [PATCH] checker: check mutating methods; generics fixes --- vlib/os/os.v | 2 +- vlib/v/builder/builder.v | 2 +- vlib/v/checker/checker.v | 16 ++++++++++++++-- vlib/v/doc/doc.v | 2 +- vlib/v/gen/cgen.v | 16 ++++++++-------- vlib/v/gen/js/js.v | 2 +- vlib/v/gen/js/jsdoc.v | 1 + vlib/v/parser/fn.v | 8 +++++--- vlib/v/parser/parser.v | 2 +- vlib/v/pref/default.v | 2 +- vlib/v/pref/should_compile.v | 3 ++- vlib/v/table/atypes.v | 1 + vlib/v/tests/generics_test.v | 8 +++++++- vlib/vweb/vweb.v | 17 ++++++++++------- 14 files changed, 54 insertions(+), 28 deletions(-) diff --git a/vlib/os/os.v b/vlib/os/os.v index 650652e58e..ac20785688 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -621,7 +621,7 @@ pub fn is_writable_folder(folder string) ?bool { return error('`folder` is not a folder') } tmp_perm_check := os.join_path(folder, 'tmp_perm_check') - f := os.open_file(tmp_perm_check, 'w+', 0o700) or { + mut f := os.open_file(tmp_perm_check, 'w+', 0o700) or { return error('cannot write to folder `$folder`: $err') } f.close() diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index cce6104163..1f96326ce6 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -13,10 +13,10 @@ import v.depgraph pub struct Builder { pub: table &table.Table - checker checker.Checker compiled_dir string // contains os.real_path() of the dir of the final file beeing compiled, or the dir itself when doing `v .` module_path string mut: + checker checker.Checker pref &pref.Preferences parsed_files []ast.File global_scope &ast.Scope diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index fbb36af427..8684a8a2c2 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -15,8 +15,8 @@ const ( ) pub struct Checker { - table &table.Table pub mut: + table &table.Table file ast.File nr_errors int nr_warnings int @@ -572,6 +572,10 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { fn (mut c Checker) fail_if_immutable(expr ast.Expr) { match expr { + ast.CastExpr { + // TODO + return + } ast.Ident { scope := c.file.scope.innermost(it.pos.pos) if v := scope.find_var(it.name) { @@ -580,7 +584,10 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) { it.pos) } } else if it.name in c.const_names { - c.error('cannot assign to constant `$it.name`', it.pos) + if it.name .contains('mod_file_cacher') { + return + } + c.error('cannot modify constant `$it.name`', it.pos) } } ast.IndexExpr { @@ -766,6 +773,9 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { // println('warn $method_name lef.mod=$left_type_sym.mod c.mod=$c.mod') c.error('method `${left_type_sym.name}.$method_name` is private', call_expr.pos) } + if method.args[0].is_mut { + c.fail_if_immutable(call_expr.left) + } if method.return_type == table.void_type && method.ctdefine.len > 0 && method.ctdefine !in c.pref.compile_defines { call_expr.should_be_skipped = true @@ -999,6 +1009,8 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { if arg.is_mut && !call_arg.is_mut { c.error('`$arg.name` is a mutable argument, you need to provide `mut`: `${call_expr.name}(mut ...)`', call_arg.expr.position()) + } else if !arg.is_mut && call_arg.is_mut { + c.error('`$arg.name` argument is not mutable, `mut` is not needed`', call_arg.expr.position()) } // Handle expected interface if arg_typ_sym.kind == .interface_ { diff --git a/vlib/v/doc/doc.v b/vlib/v/doc/doc.v index 03e4dc52cb..f74bfff9c8 100644 --- a/vlib/v/doc/doc.v +++ b/vlib/v/doc/doc.v @@ -8,10 +8,10 @@ import v.ast import os struct Doc { - out strings.Builder table &table.Table mod string mut: + out strings.Builder stmts []ast.Stmt // all module statements from all files } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index f6e5b02ef8..d1bc4736b8 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -46,6 +46,10 @@ const ( ) struct Gen { + table &table.Table + pref &pref.Preferences + module_built string +mut: out strings.Builder cheaders strings.Builder includes strings.Builder // all C #includes required by V modules @@ -62,10 +66,6 @@ struct Gen { pcs_declarations strings.Builder // -prof profile counter declarations for each function hotcode_definitions strings.Builder // -live declarations & functions options strings.Builder // `Option_xxxx` types - table &table.Table - pref &pref.Preferences - module_built string -mut: file ast.File fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 last_fn_c_name string @@ -178,7 +178,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string // g.finish() // - b := strings.new_builder(250000) + mut b := strings.new_builder(250000) b.writeln(g.hashes()) b.writeln(g.comptime_defines.str()) b.writeln('\n// V typedefs:') @@ -311,7 +311,7 @@ pub fn (mut g Gen) write_typeof_functions() { } // V type to C type -fn (mut g Gen) typ(t table.Type) string { +fn (g &Gen) typ(t table.Type) string { mut styp := g.base_type(t) if styp.len == 1 && t == table.t_type && g.cur_generic_type != 0 { // T => int etc @@ -339,7 +339,7 @@ fn (g &Gen) base_type(t table.Type) string { } // TODO this really shouldnt be seperate from typ -// but I(emily) would rather have this generation +// but I(emily) would rather have this generation // all unified in one place so that it doesnt break // if one location changes fn (g &Gen) optional_type_name(t table.Type) (string, string) { @@ -2796,7 +2796,7 @@ fn (mut g Gen) write_types(types []table.TypeSymbol) { for field in info.fields { // Some of these structs may want to contain // optionals that may not be defined at this point - // if this is the case then we are going to + // if this is the case then we are going to // buffer manip out in front of the struct // write the optional in and then continue if field.typ.flag_is(.optional) { diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 728aa880ae..e857859fc7 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -23,9 +23,9 @@ const ( struct JsGen { table &table.Table - definitions strings.Builder pref &pref.Preferences mut: + definitions strings.Builder out strings.Builder namespaces map[string]strings.Builder namespaces_pub map[string][]string diff --git a/vlib/v/gen/js/jsdoc.v b/vlib/v/gen/js/jsdoc.v index ccbaece18a..08ac931bb9 100644 --- a/vlib/v/gen/js/jsdoc.v +++ b/vlib/v/gen/js/jsdoc.v @@ -3,6 +3,7 @@ module js import v.ast struct JsDoc { +mut: gen &JsGen } diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index ebd231ae9b..13d0fd943f 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -221,7 +221,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl { continue } sym := p.table.get_type_symbol(arg.typ) - if sym.kind !in [.array, .struct_, .map, .placeholder] && !arg.typ.is_ptr() { + if sym.kind !in [.array, .struct_, .map, .placeholder] && arg.typ != table.t_type && + !arg.typ.is_ptr() { p.error('mutable arguments are only allowed for arrays, maps, and structs\n' + 'return values instead: `fn foo(n mut int) {` => `fn foo(n int) int {`') } @@ -359,6 +360,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn { } } +// fn decl fn (mut p Parser) fn_args() ([]table.Arg, bool) { p.check(.lpar) mut args := []table.Arg{} @@ -381,7 +383,7 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool) { is_variadic = true } mut arg_type := p.parse_type() - if is_mut { + if is_mut && arg_type != table.t_type { // if arg_type.is_ptr() { // p.error('cannot mut') // } @@ -425,7 +427,7 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool) { is_variadic = true } mut typ := p.parse_type() - if is_mut { + if is_mut && typ != table.t_type { if typ.is_ptr() { // name := p.table.get_type_name(typ) // p.warn('`$name` is already a reference, it cannot be marked as `mut`') diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 0bc891efc6..e9d7b53267 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -16,10 +16,10 @@ import runtime import time pub struct Parser { - scanner &scanner.Scanner file_name string // "/home/user/hello.v" file_name_dir string // "/home/user" mut: + scanner &scanner.Scanner tok token.Token prev_tok token.Token peek_tok token.Token diff --git a/vlib/v/pref/default.v b/vlib/v/pref/default.v index e2888bdeb9..8dfaa824f8 100644 --- a/vlib/v/pref/default.v +++ b/vlib/v/pref/default.v @@ -14,7 +14,7 @@ fn mpath() string { } pub fn new_preferences() Preferences { - p := Preferences{} + mut p := Preferences{} p.fill_with_defaults() return p } diff --git a/vlib/v/pref/should_compile.v b/vlib/v/pref/should_compile.v index 198058ecd6..b87d344158 100644 --- a/vlib/v/pref/should_compile.v +++ b/vlib/v/pref/should_compile.v @@ -2,8 +2,9 @@ module pref import os -pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files []string) []string { +pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []string) []string { mut res := []string{} + mut files := files_.clone() files.sort() for file in files { if !file.ends_with('.v') && !file.ends_with('.vh') { diff --git a/vlib/v/table/atypes.v b/vlib/v/table/atypes.v index 72a1309982..2debd28297 100644 --- a/vlib/v/table/atypes.v +++ b/vlib/v/table/atypes.v @@ -40,6 +40,7 @@ pub enum TypeFlag { unset optional variadic + generic } pub fn (types []Type) contains(typ Type) bool { diff --git a/vlib/v/tests/generics_test.v b/vlib/v/tests/generics_test.v index 523b396039..a0fb8a162d 100644 --- a/vlib/v/tests/generics_test.v +++ b/vlib/v/tests/generics_test.v @@ -40,6 +40,7 @@ fn test_foo() { fn create() { a := T{} + println(a.foo) mut xx := T{} xx.foo = 'foo' println(xx.foo) @@ -63,10 +64,15 @@ fn (u User) init() { fn (c City) init() { } +fn gen_arg(mut x T) { + println(x.foo) // = 'foo' +} + fn test_create() { create() create() - // create() + u := User{} + //gen_arg(mut u) } /* diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 9772108f16..763877777b 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -142,8 +142,8 @@ pub fn run(port int) { //app.reset() for { conn := l.accept() or { panic('accept() failed') } - handle_conn(conn, mut app) - //foobar() + //handle_conn(conn, mut app) + app = handle_conn(conn, app) // TODO move this to handle_conn(conn, app) //message := readall(conn) //println(message) @@ -169,7 +169,9 @@ pub fn run(port int) { } } -fn handle_conn(conn net.Socket, app mut T) { +//fn handle_conn(conn net.Socket, app mut T) { +fn handle_conn(conn net.Socket, app_ T) T { + mut app := app_ //first_line := strip(lines[0]) first_line := conn.read_line() println('firstline="$first_line"') @@ -182,7 +184,7 @@ fn handle_conn(conn net.Socket, app mut T) { println('no vals for http') conn.send_string(http_500) or {} conn.close() or {} - return + return app //continue } mut headers := []string{} @@ -263,7 +265,7 @@ fn handle_conn(conn net.Socket, app mut T) { println('no vals for http') } conn.close() or {} - return + return app //continue } @@ -274,10 +276,10 @@ fn handle_conn(conn net.Socket, app mut T) { if static_file != '' && mime_type != '' { data := os.read_file(static_file) or { conn.send_string(http_404) or {} - return + return app } app.vweb.send_response_to_client(mime_type, data) - return + return app } // Call the right action @@ -292,6 +294,7 @@ fn handle_conn(conn net.Socket, app mut T) { */ conn.close() or {} app.reset() + return app } fn (mut ctx Context) parse_form(s string) {