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')
}
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()

View File

@ -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

View File

@ -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_ {

View File

@ -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
}

View File

@ -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) {

View File

@ -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

View File

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

View File

@ -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`')

View File

@ -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

View File

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

View File

@ -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') {

View File

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

View File

@ -40,6 +40,7 @@ fn test_foo() {
fn create<T>() {
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<T>(mut x T) {
println(x.foo) // = 'foo'
}
fn test_create() {
create<User>()
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()
for {
conn := l.accept() or { panic('accept() failed') }
handle_conn<T>(conn, mut app)
//foobar<T>()
//handle_conn<T>(conn, mut app)
app = handle_conn<T>(conn, app)
// TODO move this to handle_conn<T>(conn, app)
//message := readall(conn)
//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 := conn.read_line()
println('firstline="$first_line"')
@ -182,7 +184,7 @@ fn handle_conn<T>(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<T>(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<T>(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<T>(conn net.Socket, app mut T) {
*/
conn.close() or {}
app.reset()
return app
}
fn (mut ctx Context) parse_form(s string) {