checker: verify mutability of method args
							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
 | ||||
| pub fn (f &File) read_bytes_at(size, pos int) []byte { | ||||
| 	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 [] | ||||
| 	} | ||||
|  |  | |||
|  | @ -286,6 +286,7 @@ pub mut: | |||
| 	return_type        table.Type | ||||
| 	should_be_skipped  bool | ||||
| 	generic_type       table.Type // TODO array, to support multiple types
 | ||||
| 	autofree_pregen    string | ||||
| 	// autofree_vars      []AutofreeArgVar
 | ||||
| 	// 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 { | ||||
| 	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
 | ||||
| 	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) | ||||
|  | @ -1153,6 +1176,24 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { | |||
| 						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 { | ||||
| 			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.GotoStmt {} | ||||
| 		ast.HashStmt { | ||||
| 			c.hash_stmt(node) | ||||
| 			c.hash_stmt(mut node) | ||||
| 		} | ||||
| 		ast.Import { | ||||
| 			c.import_stmt(node) | ||||
|  | @ -2358,7 +2399,7 @@ fn (mut c Checker) stmt(node ast.Stmt) { | |||
| 			c.scope_returns = true | ||||
| 		} | ||||
| 		ast.SqlStmt { | ||||
| 			c.sql_stmt(node) | ||||
| 			c.sql_stmt(mut node) | ||||
| 		} | ||||
| 		ast.StructDecl { | ||||
| 			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) | ||||
| 		} | ||||
| 		ast.PostfixExpr { | ||||
| 			return c.postfix_expr(node) | ||||
| 			return c.postfix_expr(mut node) | ||||
| 		} | ||||
| 		ast.PrefixExpr { | ||||
| 			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 | ||||
| 		} | ||||
| 		ast.SqlExpr { | ||||
| 			return c.sql_expr(node) | ||||
| 			return c.sql_expr(mut node) | ||||
| 		} | ||||
| 		ast.StringLiteral { | ||||
| 			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
 | ||||
| 		saved_mod := ident.mod | ||||
| 		ident.mod = 'builtin' | ||||
| 		builtin_type := c.ident(ident) | ||||
| 		builtin_type := c.ident(mut ident) | ||||
| 		if builtin_type != table.void_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 { | ||||
| 	for id in node.lockeds { | ||||
| 		c.ident(mut id) | ||||
| 	for i, id in node.lockeds { | ||||
| 		// c.ident(mut id)
 | ||||
| 		c.ident(mut node.lockeds[i]) | ||||
| 		if id.obj is ast.Var as v { | ||||
| 			if v.typ.share() != .shared_t { | ||||
| 				c.error('`$id.name` must be declared `shared` to be locked', id.pos) | ||||
|  |  | |||
|  | @ -101,6 +101,7 @@ mut: | |||
| 	inside_return         bool | ||||
| 	inside_or_block       bool | ||||
| 	strs_to_free          []string // strings.Builder
 | ||||
| 	strs_to_free0         []string // strings.Builder
 | ||||
| 	inside_call           bool | ||||
| 	has_main              bool | ||||
| 	inside_const          bool | ||||
|  | @ -1915,7 +1916,21 @@ fn (mut g Gen) expr(node ast.Expr) { | |||
| 			g.write(node.val.str()) | ||||
| 		} | ||||
| 		ast.CallExpr { | ||||
| 			// if g.fileis('1.strings') {
 | ||||
| 			// println('\ncall_expr()()')
 | ||||
| 			// }
 | ||||
| 			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 { | ||||
| 			// 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 {
 | ||||
| 	// g.write('/*${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()`
 | ||||
| 		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)`
 | ||||
| 	if is_print && node.args[0].typ != table.string_type { // && !free_tmp_arg_vars {
 | ||||
| 		typ := node.args[0].typ | ||||
|  | @ -614,7 +617,10 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { | |||
| 	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,
 | ||||
| 	// like `foo(get_string())` or `foo(a + b)`
 | ||||
| 	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' | ||||
| 			g.called_fn_name = name | ||||
| 			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)
 | ||||
| 			// println('cur line ="$cur_line"')
 | ||||
| 			// 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()) | ||||
| 		} | ||||
| 	} | ||||
| 	node := ast.Import{ | ||||
| 	mut node := ast.Import{ | ||||
| 		pos: pos | ||||
| 		mod: mod_name | ||||
| 		alias: mod_alias | ||||
| 	} | ||||
| 	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
 | ||||
| 	} | ||||
| 	pos_t := p.tok.position() | ||||
|  |  | |||
|  | @ -34,6 +34,13 @@ fn str_tmp_expr() { | |||
| } | ||||
| 
 | ||||
| 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
 | ||||
| } | ||||
| 
 | ||||
|  | @ -112,3 +119,11 @@ fn main() { | |||
| 	// str_replace()
 | ||||
| 	println('end') | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| s := s.replace().replace() | ||||
| tmp := s.replace() | ||||
| s.free() | ||||
| s = tmp.replace() | ||||
| tmp.free() | ||||
| */ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue