mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
checker: verify mutability of method args
This commit is contained in:
parent
914c1a527d
commit
e2c7126d11
@ -86,7 +86,7 @@ pub fn (f &File) read_bytes(size int) []byte {
|
|||||||
// read_bytes_at reads bytes at the given position in the file
|
// read_bytes_at reads bytes at the given position in the file
|
||||||
pub fn (f &File) read_bytes_at(size, pos int) []byte {
|
pub fn (f &File) read_bytes_at(size, pos int) []byte {
|
||||||
mut arr := []byte{len: size}
|
mut arr := []byte{len: size}
|
||||||
nreadbytes := f.read_bytes_into(pos, arr) or {
|
nreadbytes := f.read_bytes_into(pos, mut arr) or {
|
||||||
// return err
|
// return err
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -286,6 +286,7 @@ pub mut:
|
|||||||
return_type table.Type
|
return_type table.Type
|
||||||
should_be_skipped bool
|
should_be_skipped bool
|
||||||
generic_type table.Type // TODO array, to support multiple types
|
generic_type table.Type // TODO array, to support multiple types
|
||||||
|
autofree_pregen string
|
||||||
// autofree_vars []AutofreeArgVar
|
// autofree_vars []AutofreeArgVar
|
||||||
// autofree_vars_ids []int
|
// autofree_vars_ids []int
|
||||||
}
|
}
|
||||||
|
@ -934,7 +934,30 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
|
|||||||
|
|
||||||
pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
|
pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
|
||||||
c.stmts(call_expr.or_block.stmts)
|
c.stmts(call_expr.or_block.stmts)
|
||||||
typ := if call_expr.is_method { c.call_method(call_expr) } else { c.call_fn(call_expr) }
|
// First check everything that applies to both fns and methods
|
||||||
|
// TODO merge logic from call_method and call_fn
|
||||||
|
/*
|
||||||
|
for i, call_arg in call_expr.args {
|
||||||
|
if call_arg.is_mut {
|
||||||
|
c.fail_if_immutable(call_arg.expr)
|
||||||
|
if !arg.is_mut {
|
||||||
|
tok := call_arg.share.str()
|
||||||
|
c.error('`$call_expr.name` parameter `$arg.name` is not `$tok`, `$tok` is not needed`',
|
||||||
|
call_arg.expr.position())
|
||||||
|
} else if arg.typ.share() != call_arg.share {
|
||||||
|
c.error('wrong shared type', call_arg.expr.position())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if arg.is_mut && (!call_arg.is_mut || arg.typ.share() != call_arg.share) {
|
||||||
|
tok := call_arg.share.str()
|
||||||
|
c.error('`$call_expr.name` parameter `$arg.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${i+1}`',
|
||||||
|
call_arg.expr.position())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// Now call `call_method` or `call_fn` for specific checks.
|
||||||
|
typ := if call_expr.is_method { c.call_method(mut call_expr) } else { c.call_fn(mut call_expr) }
|
||||||
// autofree
|
// autofree
|
||||||
free_tmp_arg_vars := c.pref.autofree && c.pref.experimental && !c.is_builtin_mod &&
|
free_tmp_arg_vars := c.pref.autofree && c.pref.experimental && !c.is_builtin_mod &&
|
||||||
call_expr.args.len > 0 && !call_expr.args[0].typ.has_flag(.optional)
|
call_expr.args.len > 0 && !call_expr.args[0].typ.has_flag(.optional)
|
||||||
@ -1153,6 +1176,24 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||||||
call_expr.pos)
|
call_expr.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
param := if method.is_variadic && i >= method.args.len - 1 { method.args[method.args.len -
|
||||||
|
1] } else { method.args[i + 1] }
|
||||||
|
if arg.is_mut {
|
||||||
|
c.fail_if_immutable(arg.expr)
|
||||||
|
if !param.is_mut {
|
||||||
|
tok := arg.share.str()
|
||||||
|
c.error('`$call_expr.name` parameter `$param.name` is not `$tok`, `$tok` is not needed`',
|
||||||
|
arg.expr.position())
|
||||||
|
} else if param.typ.share() != arg.share {
|
||||||
|
c.error('wrong shared type', arg.expr.position())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if param.is_mut && (!arg.is_mut || param.typ.share() != arg.share) {
|
||||||
|
tok := arg.share.str()
|
||||||
|
c.error('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${i+1}`',
|
||||||
|
arg.expr.position())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if method.is_unsafe && !c.inside_unsafe {
|
if method.is_unsafe && !c.inside_unsafe {
|
||||||
c.warn('method `${left_type_sym.source_name}.$method_name` must be called from an `unsafe` block',
|
c.warn('method `${left_type_sym.source_name}.$method_name` must be called from an `unsafe` block',
|
||||||
@ -2339,7 +2380,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
|||||||
ast.GotoLabel {}
|
ast.GotoLabel {}
|
||||||
ast.GotoStmt {}
|
ast.GotoStmt {}
|
||||||
ast.HashStmt {
|
ast.HashStmt {
|
||||||
c.hash_stmt(node)
|
c.hash_stmt(mut node)
|
||||||
}
|
}
|
||||||
ast.Import {
|
ast.Import {
|
||||||
c.import_stmt(node)
|
c.import_stmt(node)
|
||||||
@ -2358,7 +2399,7 @@ fn (mut c Checker) stmt(node ast.Stmt) {
|
|||||||
c.scope_returns = true
|
c.scope_returns = true
|
||||||
}
|
}
|
||||||
ast.SqlStmt {
|
ast.SqlStmt {
|
||||||
c.sql_stmt(node)
|
c.sql_stmt(mut node)
|
||||||
}
|
}
|
||||||
ast.StructDecl {
|
ast.StructDecl {
|
||||||
c.struct_decl(node)
|
c.struct_decl(node)
|
||||||
@ -2663,7 +2704,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
|||||||
return c.match_expr(mut node)
|
return c.match_expr(mut node)
|
||||||
}
|
}
|
||||||
ast.PostfixExpr {
|
ast.PostfixExpr {
|
||||||
return c.postfix_expr(node)
|
return c.postfix_expr(mut node)
|
||||||
}
|
}
|
||||||
ast.PrefixExpr {
|
ast.PrefixExpr {
|
||||||
right_type := c.expr(node.right)
|
right_type := c.expr(node.right)
|
||||||
@ -2731,7 +2772,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
|||||||
return table.u32_type
|
return table.u32_type
|
||||||
}
|
}
|
||||||
ast.SqlExpr {
|
ast.SqlExpr {
|
||||||
return c.sql_expr(node)
|
return c.sql_expr(mut node)
|
||||||
}
|
}
|
||||||
ast.StringLiteral {
|
ast.StringLiteral {
|
||||||
if node.language == .c {
|
if node.language == .c {
|
||||||
@ -2906,7 +2947,7 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
|
|||||||
// main.compare_f32 may actually be builtin.compare_f32
|
// main.compare_f32 may actually be builtin.compare_f32
|
||||||
saved_mod := ident.mod
|
saved_mod := ident.mod
|
||||||
ident.mod = 'builtin'
|
ident.mod = 'builtin'
|
||||||
builtin_type := c.ident(ident)
|
builtin_type := c.ident(mut ident)
|
||||||
if builtin_type != table.void_type {
|
if builtin_type != table.void_type {
|
||||||
return builtin_type
|
return builtin_type
|
||||||
}
|
}
|
||||||
@ -3182,8 +3223,9 @@ pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) table.Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type {
|
pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) table.Type {
|
||||||
for id in node.lockeds {
|
for i, id in node.lockeds {
|
||||||
c.ident(mut id)
|
// c.ident(mut id)
|
||||||
|
c.ident(mut node.lockeds[i])
|
||||||
if id.obj is ast.Var as v {
|
if id.obj is ast.Var as v {
|
||||||
if v.typ.share() != .shared_t {
|
if v.typ.share() != .shared_t {
|
||||||
c.error('`$id.name` must be declared `shared` to be locked', id.pos)
|
c.error('`$id.name` must be declared `shared` to be locked', id.pos)
|
||||||
|
@ -101,6 +101,7 @@ mut:
|
|||||||
inside_return bool
|
inside_return bool
|
||||||
inside_or_block bool
|
inside_or_block bool
|
||||||
strs_to_free []string // strings.Builder
|
strs_to_free []string // strings.Builder
|
||||||
|
strs_to_free0 []string // strings.Builder
|
||||||
inside_call bool
|
inside_call bool
|
||||||
has_main bool
|
has_main bool
|
||||||
inside_const bool
|
inside_const bool
|
||||||
@ -1915,7 +1916,21 @@ fn (mut g Gen) expr(node ast.Expr) {
|
|||||||
g.write(node.val.str())
|
g.write(node.val.str())
|
||||||
}
|
}
|
||||||
ast.CallExpr {
|
ast.CallExpr {
|
||||||
|
// if g.fileis('1.strings') {
|
||||||
|
// println('\ncall_expr()()')
|
||||||
|
// }
|
||||||
g.call_expr(node)
|
g.call_expr(node)
|
||||||
|
// if g.fileis('1.strings') {
|
||||||
|
// println('before:' + node.autofree_pregen)
|
||||||
|
// }
|
||||||
|
if g.pref.autofree && node.autofree_pregen != '' { // g.strs_to_free0.len != 0 {
|
||||||
|
// g.insert_before_stmt('/*START2*/' + g.strs_to_free0.join('\n') + '/*END*/')
|
||||||
|
g.insert_before_stmt('/*START3*/' + node.autofree_pregen + '/*END*/')
|
||||||
|
// for s in g.strs_to_free0 {
|
||||||
|
// //g.writeln(s)
|
||||||
|
// }
|
||||||
|
g.strs_to_free0 = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast.CastExpr {
|
ast.CastExpr {
|
||||||
// g.write('/*cast*/')
|
// g.write('/*cast*/')
|
||||||
|
@ -419,7 +419,9 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.generate_tmp_autofree_arg_vars(node, name)
|
// TODO2
|
||||||
|
// g.generate_tmp_autofree_arg_vars(mut node, name)
|
||||||
|
//
|
||||||
// if node.receiver_type != 0 {
|
// if node.receiver_type != 0 {
|
||||||
// g.write('/*${g.typ(node.receiver_type)}*/')
|
// g.write('/*${g.typ(node.receiver_type)}*/')
|
||||||
// g.write('/*expr_type=${g.typ(node.left_type)} rec type=${g.typ(node.receiver_type)}*/')
|
// g.write('/*expr_type=${g.typ(node.left_type)} rec type=${g.typ(node.receiver_type)}*/')
|
||||||
@ -534,7 +536,8 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
|||||||
// `foo<int>()` => `foo_int()`
|
// `foo<int>()` => `foo_int()`
|
||||||
name += '_' + g.typ(node.generic_type)
|
name += '_' + g.typ(node.generic_type)
|
||||||
}
|
}
|
||||||
g.generate_tmp_autofree_arg_vars(node, name)
|
// TODO2
|
||||||
|
// g.generate_tmp_autofree_arg_vars(node, name)
|
||||||
// Handle `print(x)`
|
// Handle `print(x)`
|
||||||
if is_print && node.args[0].typ != table.string_type { // && !free_tmp_arg_vars {
|
if is_print && node.args[0].typ != table.string_type { // && !free_tmp_arg_vars {
|
||||||
typ := node.args[0].typ
|
typ := node.args[0].typ
|
||||||
@ -614,7 +617,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
|
|||||||
g.is_json_fn = false
|
g.is_json_fn = false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) generate_tmp_autofree_arg_vars(node ast.CallExpr, name string) {
|
fn (mut g Gen) generate_tmp_autofree_arg_vars(mut node ast.CallExpr, name string) {
|
||||||
|
// if g.fileis('1.strings') {
|
||||||
|
// println('gen tmp autofree()')
|
||||||
|
// }
|
||||||
// Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression,
|
// Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression,
|
||||||
// like `foo(get_string())` or `foo(a + b)`
|
// like `foo(get_string())` or `foo(a + b)`
|
||||||
mut free_tmp_arg_vars := g.autofree && g.pref.experimental && !g.is_builtin_mod &&
|
mut free_tmp_arg_vars := g.autofree && g.pref.experimental && !g.is_builtin_mod &&
|
||||||
@ -633,7 +639,10 @@ fn (mut g Gen) generate_tmp_autofree_arg_vars(node ast.CallExpr, name string) {
|
|||||||
t := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
|
t := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
|
||||||
g.called_fn_name = name
|
g.called_fn_name = name
|
||||||
str_expr := g.write_expr_to_string(arg.expr)
|
str_expr := g.write_expr_to_string(arg.expr)
|
||||||
g.insert_before_stmt('string $t = $str_expr; // new4. to free arg #$i name=$name')
|
// g.insert_before_stmt('string $t = $str_expr; // new4. to free arg #$i name=$name')
|
||||||
|
// g.strs_to_free0 << 'string $t = $str_expr; // new5. to free arg #$i name=$name'
|
||||||
|
node.autofree_pregen += 'string $t = $str_expr; // new6. to free arg #$i name=$name\n'
|
||||||
|
// println('setting pregen to ' + node.autofree_pregen)
|
||||||
// cur_line = g.go_before_stmt(0)
|
// cur_line = g.go_before_stmt(0)
|
||||||
// println('cur line ="$cur_line"')
|
// println('cur line ="$cur_line"')
|
||||||
// g.writeln('string $t = $str_expr; // new3. to free arg #$i name=$name')
|
// g.writeln('string $t = $str_expr; // new3. to free arg #$i name=$name')
|
||||||
|
@ -1501,13 +1501,13 @@ fn (mut p Parser) import_stmt() ast.Import {
|
|||||||
p.error_with_pos('import alias `$mod_name as $mod_alias` is redundant', p.prev_tok.position())
|
p.error_with_pos('import alias `$mod_name as $mod_alias` is redundant', p.prev_tok.position())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node := ast.Import{
|
mut node := ast.Import{
|
||||||
pos: pos
|
pos: pos
|
||||||
mod: mod_name
|
mod: mod_name
|
||||||
alias: mod_alias
|
alias: mod_alias
|
||||||
}
|
}
|
||||||
if p.tok.kind == .lcbr { // import module { fn1, Type2 } syntax
|
if p.tok.kind == .lcbr { // import module { fn1, Type2 } syntax
|
||||||
p.import_syms(node)
|
p.import_syms(mut node)
|
||||||
p.register_used_import(mod_name) // no `unused import` msg for parent
|
p.register_used_import(mod_name) // no `unused import` msg for parent
|
||||||
}
|
}
|
||||||
pos_t := p.tok.position()
|
pos_t := p.tok.position()
|
||||||
|
@ -34,6 +34,13 @@ fn str_tmp_expr() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn str_tmp_expr_advanced() {
|
fn str_tmp_expr_advanced() {
|
||||||
|
// t1 = 'c' + 'd'
|
||||||
|
// t2 = 'e + f'
|
||||||
|
// t3 = add_strings(t2, 'g')
|
||||||
|
// handle_strings(t1, t3)
|
||||||
|
// t1.free()
|
||||||
|
// t2.free()
|
||||||
|
// t3.free()
|
||||||
// handle_strings('c' + 'd', add_strings('e' + 'f', 'g')) // both lvl 1 and lvl2 exprs must be freed
|
// handle_strings('c' + 'd', add_strings('e' + 'f', 'g')) // both lvl 1 and lvl2 exprs must be freed
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,3 +119,11 @@ fn main() {
|
|||||||
// str_replace()
|
// str_replace()
|
||||||
println('end')
|
println('end')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
s := s.replace().replace()
|
||||||
|
tmp := s.replace()
|
||||||
|
s.free()
|
||||||
|
s = tmp.replace()
|
||||||
|
tmp.free()
|
||||||
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user