checker: check mutating methods; generics fixes

pull/5159/head
Alexander Medvednikov 2020-06-01 15:43:54 +02:00
parent 945439dab6
commit 3d83934caf
14 changed files with 54 additions and 28 deletions

View File

@ -621,7 +621,7 @@ pub fn is_writable_folder(folder string) ?bool {
return error('`folder` is not a folder') return error('`folder` is not a folder')
} }
tmp_perm_check := os.join_path(folder, 'tmp_perm_check') 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') return error('cannot write to folder `$folder`: $err')
} }
f.close() f.close()

View File

@ -13,10 +13,10 @@ import v.depgraph
pub struct Builder { pub struct Builder {
pub: pub:
table &table.Table 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 .` 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 module_path string
mut: mut:
checker checker.Checker
pref &pref.Preferences pref &pref.Preferences
parsed_files []ast.File parsed_files []ast.File
global_scope &ast.Scope global_scope &ast.Scope

View File

@ -15,8 +15,8 @@ const (
) )
pub struct Checker { pub struct Checker {
table &table.Table
pub mut: pub mut:
table &table.Table
file ast.File file ast.File
nr_errors int nr_errors int
nr_warnings 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) { fn (mut c Checker) fail_if_immutable(expr ast.Expr) {
match expr { match expr {
ast.CastExpr {
// TODO
return
}
ast.Ident { ast.Ident {
scope := c.file.scope.innermost(it.pos.pos) scope := c.file.scope.innermost(it.pos.pos)
if v := scope.find_var(it.name) { if v := scope.find_var(it.name) {
@ -580,7 +584,10 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) {
it.pos) it.pos)
} }
} else if it.name in c.const_names { } 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 { 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') // 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) 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 if method.return_type == table.void_type && method.ctdefine.len > 0 && method.ctdefine !in
c.pref.compile_defines { c.pref.compile_defines {
call_expr.should_be_skipped = true 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 { 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 ...)`', c.error('`$arg.name` is a mutable argument, you need to provide `mut`: `${call_expr.name}(mut ...)`',
call_arg.expr.position()) 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 // Handle expected interface
if arg_typ_sym.kind == .interface_ { if arg_typ_sym.kind == .interface_ {

View File

@ -8,10 +8,10 @@ import v.ast
import os import os
struct Doc { struct Doc {
out strings.Builder
table &table.Table table &table.Table
mod string mod string
mut: mut:
out strings.Builder
stmts []ast.Stmt // all module statements from all files stmts []ast.Stmt // all module statements from all files
} }

View File

@ -46,6 +46,10 @@ const (
) )
struct Gen { struct Gen {
table &table.Table
pref &pref.Preferences
module_built string
mut:
out strings.Builder out strings.Builder
cheaders strings.Builder cheaders strings.Builder
includes strings.Builder // all C #includes required by V modules 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 pcs_declarations strings.Builder // -prof profile counter declarations for each function
hotcode_definitions strings.Builder // -live declarations & functions hotcode_definitions strings.Builder // -live declarations & functions
options strings.Builder // `Option_xxxx` types options strings.Builder // `Option_xxxx` types
table &table.Table
pref &pref.Preferences
module_built string
mut:
file ast.File file ast.File
fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
last_fn_c_name string last_fn_c_name string
@ -178,7 +178,7 @@ pub fn cgen(files []ast.File, table &table.Table, pref &pref.Preferences) string
// //
g.finish() g.finish()
// //
b := strings.new_builder(250000) mut b := strings.new_builder(250000)
b.writeln(g.hashes()) b.writeln(g.hashes())
b.writeln(g.comptime_defines.str()) b.writeln(g.comptime_defines.str())
b.writeln('\n// V typedefs:') b.writeln('\n// V typedefs:')
@ -311,7 +311,7 @@ pub fn (mut g Gen) write_typeof_functions() {
} }
// V type to C type // 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) mut styp := g.base_type(t)
if styp.len == 1 && t == table.t_type && g.cur_generic_type != 0 { if styp.len == 1 && t == table.t_type && g.cur_generic_type != 0 {
// T => int etc // T => int etc
@ -339,7 +339,7 @@ fn (g &Gen) base_type(t table.Type) string {
} }
// TODO this really shouldnt be seperate from typ // 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 // all unified in one place so that it doesnt break
// if one location changes // if one location changes
fn (g &Gen) optional_type_name(t table.Type) (string, string) { 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 { for field in info.fields {
// Some of these structs may want to contain // Some of these structs may want to contain
// optionals that may not be defined at this point // 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 // buffer manip out in front of the struct
// write the optional in and then continue // write the optional in and then continue
if field.typ.flag_is(.optional) { if field.typ.flag_is(.optional) {

View File

@ -23,9 +23,9 @@ const (
struct JsGen { struct JsGen {
table &table.Table table &table.Table
definitions strings.Builder
pref &pref.Preferences pref &pref.Preferences
mut: mut:
definitions strings.Builder
out strings.Builder out strings.Builder
namespaces map[string]strings.Builder namespaces map[string]strings.Builder
namespaces_pub map[string][]string namespaces_pub map[string][]string

View File

@ -3,6 +3,7 @@ module js
import v.ast import v.ast
struct JsDoc { struct JsDoc {
mut:
gen &JsGen gen &JsGen
} }

View File

@ -221,7 +221,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
continue continue
} }
sym := p.table.get_type_symbol(arg.typ) 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' + 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 {`') '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) { fn (mut p Parser) fn_args() ([]table.Arg, bool) {
p.check(.lpar) p.check(.lpar)
mut args := []table.Arg{} mut args := []table.Arg{}
@ -381,7 +383,7 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool) {
is_variadic = true is_variadic = true
} }
mut arg_type := p.parse_type() mut arg_type := p.parse_type()
if is_mut { if is_mut && arg_type != table.t_type {
// if arg_type.is_ptr() { // if arg_type.is_ptr() {
// p.error('cannot mut') // p.error('cannot mut')
// } // }
@ -425,7 +427,7 @@ fn (mut p Parser) fn_args() ([]table.Arg, bool) {
is_variadic = true is_variadic = true
} }
mut typ := p.parse_type() mut typ := p.parse_type()
if is_mut { if is_mut && typ != table.t_type {
if typ.is_ptr() { if typ.is_ptr() {
// name := p.table.get_type_name(typ) // name := p.table.get_type_name(typ)
// p.warn('`$name` is already a reference, it cannot be marked as `mut`') // p.warn('`$name` is already a reference, it cannot be marked as `mut`')

View File

@ -16,10 +16,10 @@ import runtime
import time import time
pub struct Parser { pub struct Parser {
scanner &scanner.Scanner
file_name string // "/home/user/hello.v" file_name string // "/home/user/hello.v"
file_name_dir string // "/home/user" file_name_dir string // "/home/user"
mut: mut:
scanner &scanner.Scanner
tok token.Token tok token.Token
prev_tok token.Token prev_tok token.Token
peek_tok token.Token peek_tok token.Token

View File

@ -14,7 +14,7 @@ fn mpath() string {
} }
pub fn new_preferences() Preferences { pub fn new_preferences() Preferences {
p := Preferences{} mut p := Preferences{}
p.fill_with_defaults() p.fill_with_defaults()
return p return p
} }

View File

@ -2,8 +2,9 @@ module pref
import os 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 res := []string{}
mut files := files_.clone()
files.sort() files.sort()
for file in files { for file in files {
if !file.ends_with('.v') && !file.ends_with('.vh') { if !file.ends_with('.v') && !file.ends_with('.vh') {

View File

@ -40,6 +40,7 @@ pub enum TypeFlag {
unset unset
optional optional
variadic variadic
generic
} }
pub fn (types []Type) contains(typ Type) bool { pub fn (types []Type) contains(typ Type) bool {

View File

@ -40,6 +40,7 @@ fn test_foo() {
fn create<T>() { fn create<T>() {
a := T{} a := T{}
println(a.foo)
mut xx := T{} mut xx := T{}
xx.foo = 'foo' xx.foo = 'foo'
println(xx.foo) println(xx.foo)
@ -63,10 +64,15 @@ fn (u User) init() {
fn (c City) init() { fn (c City) init() {
} }
fn gen_arg<T>(mut x T) {
println(x.foo) // = 'foo'
}
fn test_create() { fn test_create() {
create<User>() create<User>()
create<City>() create<City>()
// create<User>() u := User{}
//gen_arg<User>(mut u)
} }
/* /*

View File

@ -142,8 +142,8 @@ pub fn run<T>(port int) {
//app.reset() //app.reset()
for { for {
conn := l.accept() or { panic('accept() failed') } conn := l.accept() or { panic('accept() failed') }
handle_conn<T>(conn, mut app) //handle_conn<T>(conn, mut app)
//foobar<T>() app = handle_conn<T>(conn, app)
// TODO move this to handle_conn<T>(conn, app) // TODO move this to handle_conn<T>(conn, app)
//message := readall(conn) //message := readall(conn)
//println(message) //println(message)
@ -169,7 +169,9 @@ pub fn run<T>(port int) {
} }
} }
fn handle_conn<T>(conn net.Socket, app mut T) { //fn handle_conn<T>(conn net.Socket, app mut T) {
fn handle_conn<T>(conn net.Socket, app_ T) T {
mut app := app_
//first_line := strip(lines[0]) //first_line := strip(lines[0])
first_line := conn.read_line() first_line := conn.read_line()
println('firstline="$first_line"') println('firstline="$first_line"')
@ -182,7 +184,7 @@ fn handle_conn<T>(conn net.Socket, app mut T) {
println('no vals for http') println('no vals for http')
conn.send_string(http_500) or {} conn.send_string(http_500) or {}
conn.close() or {} conn.close() or {}
return return app
//continue //continue
} }
mut headers := []string{} mut headers := []string{}
@ -263,7 +265,7 @@ fn handle_conn<T>(conn net.Socket, app mut T) {
println('no vals for http') println('no vals for http')
} }
conn.close() or {} conn.close() or {}
return return app
//continue //continue
} }
@ -274,10 +276,10 @@ fn handle_conn<T>(conn net.Socket, app mut T) {
if static_file != '' && mime_type != '' { if static_file != '' && mime_type != '' {
data := os.read_file(static_file) or { data := os.read_file(static_file) or {
conn.send_string(http_404) or {} conn.send_string(http_404) or {}
return return app
} }
app.vweb.send_response_to_client(mime_type, data) app.vweb.send_response_to_client(mime_type, data)
return return app
} }
// Call the right action // Call the right action
@ -292,6 +294,7 @@ fn handle_conn<T>(conn net.Socket, app mut T) {
*/ */
conn.close() or {} conn.close() or {}
app.reset() app.reset()
return app
} }
fn (mut ctx Context) parse_form(s string) { fn (mut ctx Context) parse_form(s string) {