checker: check array and fields mutability
							parent
							
								
									ce1215f507
								
							
						
					
					
						commit
						682838a0cf
					
				|  | @ -24,8 +24,8 @@ const ( | |||
| 		'vlib/v/tests/live_test.v', // Linux & Solaris only; since live does not actually work for now with v2, just skip
 | ||||
| 		'vlib/v/tests/asm_test.v', // skip everywhere for now, works on linux with cc != tcc
 | ||||
| 	] | ||||
| 	skip_on_linux = []string | ||||
| 	skip_on_non_linux = []string | ||||
| 	skip_on_linux = []string{} | ||||
| 	skip_on_non_linux = []string{} | ||||
| ) | ||||
| 
 | ||||
| fn main() { | ||||
|  |  | |||
|  | @ -141,7 +141,7 @@ fn test_bf_from_str() { | |||
| 	rand.seed(time.now().unix) | ||||
| 	len := 80 | ||||
| 	mut input := '' | ||||
| 	for i in 0..len { | ||||
| 	for _ in 0..len { | ||||
| 		if rand.next(2) == 1 { | ||||
| 			input = input + '1' | ||||
| 		} | ||||
|  | @ -244,7 +244,7 @@ fn test_bf_resize() { | |||
| 	rand.seed(time.now().unix) | ||||
| 	len := 80 | ||||
| 	mut input := bitfield.new(rand.next(len) + 1) | ||||
| 	for i in 0..100 { | ||||
| 	for _ in 0..100 { | ||||
| 		input.resize(rand.next(len) + 1) | ||||
| 		input.setbit(input.getsize() - 1) | ||||
| 	} | ||||
|  |  | |||
|  | @ -7,11 +7,12 @@ import strings | |||
| 
 | ||||
| pub struct array { | ||||
| pub: | ||||
| 	element_size int | ||||
| pub mut: | ||||
| 	data         voidptr// Using a void pointer allows to implement arrays without generics and without generating
 | ||||
| // extra code for every type.
 | ||||
| 	len          int | ||||
| 	cap          int | ||||
| 	element_size int | ||||
| } | ||||
| 
 | ||||
| // Internal function, used by V (`nums := []int`)
 | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ pub mut: | |||
| } | ||||
| 
 | ||||
| pub struct Line64 { | ||||
| pub mut: | ||||
| 	f_size_of_struct u32 | ||||
| 	f_key voidptr | ||||
| 	f_line_number u32 | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ struct User { | |||
| } | ||||
| 
 | ||||
| struct A { | ||||
| mut: | ||||
| 	m map[string]int | ||||
| 	users map[string]User | ||||
| } | ||||
|  | @ -24,7 +25,7 @@ fn test_map() { | |||
| 	assert 'hi' in m | ||||
| 	mut sum := 0 | ||||
| 	// Test `for in`
 | ||||
| 	for key, val in m { | ||||
| 	for _, val in m { | ||||
| 		sum += val | ||||
| 	} | ||||
| 	assert sum == 80 + 101 | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ pub: | |||
| 	// hash_cache int
 | ||||
| 
 | ||||
| pub struct ustring { | ||||
| pub: | ||||
| pub mut: | ||||
| 	s     string | ||||
| 	runes []int | ||||
| 	len   int | ||||
|  |  | |||
|  | @ -435,7 +435,7 @@ pub fn (fs FlagParser) usage() string { | |||
| 	if fs.flags.len > 0 { | ||||
| 		use += 'Options:\n' | ||||
| 		for f in fs.flags { | ||||
| 			mut onames:=[]string | ||||
| 			mut onames := []string{} | ||||
| 			if f.abbr != 0 { | ||||
| 				onames << '-${f.abbr.str()}' | ||||
| 			} | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ fn test_rand_r_seed_update() { | |||
| 
 | ||||
| 	for _ in 0..rnd_count { | ||||
| 		prev_seed := seed | ||||
| 		res := rand.rand_r(&seed) | ||||
| 		_ := rand.rand_r(&seed) | ||||
| 
 | ||||
| 		assert prev_seed != seed | ||||
| 	} | ||||
|  |  | |||
|  | @ -4,8 +4,9 @@ | |||
| module time | ||||
| 
 | ||||
| pub struct StopWatch { | ||||
| mut: | ||||
| 	pause_time u64 | ||||
| pub: | ||||
| pub mut: | ||||
| 	start      u64 | ||||
| 	end        u64 | ||||
| } | ||||
|  |  | |||
|  | @ -134,9 +134,10 @@ mut: | |||
| 
 | ||||
| pub struct ConstDecl { | ||||
| pub: | ||||
| 	fields []ConstField | ||||
| 	is_pub bool | ||||
| 	pos    token.Position | ||||
| pub mut: | ||||
| 	fields []ConstField | ||||
| } | ||||
| 
 | ||||
| pub struct StructDecl { | ||||
|  | @ -162,10 +163,10 @@ pub: | |||
| 
 | ||||
| pub struct StructInitField { | ||||
| pub: | ||||
| 	name          string | ||||
| 	expr          Expr | ||||
| 	pos           token.Position | ||||
| mut: | ||||
| 	name          string | ||||
| 	typ           table.Type | ||||
| 	expected_type table.Type | ||||
| } | ||||
|  | @ -173,10 +174,10 @@ mut: | |||
| pub struct StructInit { | ||||
| pub: | ||||
| 	pos      token.Position | ||||
| 	fields   []StructInitField | ||||
| 	is_short bool | ||||
| mut: | ||||
| 	typ      table.Type | ||||
| 	fields   []StructInitField | ||||
| } | ||||
| 
 | ||||
| // import statement
 | ||||
|  | @ -198,7 +199,6 @@ pub struct FnDecl { | |||
| pub: | ||||
| 	name          string | ||||
| 	stmts         []Stmt | ||||
| 	return_type   table.Type | ||||
| 	args          []table.Arg | ||||
| 	is_deprecated bool | ||||
| 	is_pub        bool | ||||
|  | @ -213,6 +213,8 @@ pub: | |||
| 	is_builtin    bool // this function is defined in builtin/strconv
 | ||||
| 	ctdefine      string // has [if myflag] tag
 | ||||
| 	pos           token.Position | ||||
| pub mut: | ||||
| 	return_type   table.Type | ||||
| } | ||||
| 
 | ||||
| pub struct BranchStmt { | ||||
|  | @ -224,10 +226,10 @@ pub struct CallExpr { | |||
| pub: | ||||
| 	pos                token.Position | ||||
| 	left               Expr // `user` in `user.register()`
 | ||||
| 	is_method          bool | ||||
| 	mod                string | ||||
| mut: | ||||
| 	name               string | ||||
| 	is_method          bool | ||||
| 	args               []CallArg | ||||
| 	expected_arg_types []table.Type | ||||
| 	is_c               bool | ||||
|  | @ -292,10 +294,11 @@ pub struct File { | |||
| pub: | ||||
| 	path         string | ||||
| 	mod          Module | ||||
| 	imports      []Import | ||||
| 	stmts        []Stmt | ||||
| 	scope        &Scope | ||||
| 	global_scope &Scope | ||||
| mut: | ||||
| 	imports      []Import | ||||
| } | ||||
| 
 | ||||
| pub struct IdentFn { | ||||
|  | @ -331,11 +334,11 @@ pub: | |||
| 	tok_kind token.Kind | ||||
| 	mod      string | ||||
| 	pos      token.Position | ||||
| 	is_mut   bool | ||||
| mut: | ||||
| 	name     string | ||||
| 	kind     IdentKind | ||||
| 	info     IdentInfo | ||||
| 	is_mut   bool | ||||
| } | ||||
| 
 | ||||
| pub fn (i &Ident) var_info() IdentVar { | ||||
|  | @ -508,11 +511,11 @@ pub: | |||
| 
 | ||||
| pub struct AssignStmt { | ||||
| pub: | ||||
| 	left        []Ident | ||||
| 	right       []Expr | ||||
| 	op          token.Kind | ||||
| 	pos         token.Position | ||||
| mut: | ||||
| pub mut: | ||||
| 	left        []Ident | ||||
| 	left_types  []table.Type | ||||
| 	right_types []table.Type | ||||
| 	is_static   bool // for translated code only
 | ||||
|  | @ -670,8 +673,8 @@ pub: | |||
| 	expr      Expr // `buf`
 | ||||
| 	arg       Expr // `n` in `string(buf, n)`
 | ||||
| 	typ       table.Type // `string`
 | ||||
| 	typname   string | ||||
| mut: | ||||
| 	typname   string | ||||
| 	expr_type table.Type // `byteptr`
 | ||||
| 	has_arg   bool | ||||
| } | ||||
|  |  | |||
|  | @ -121,7 +121,7 @@ pub fn (x Expr) str() string { | |||
| 			return '${it.expr.str()}.${it.field}' | ||||
| 		} | ||||
| 		StringInterLiteral { | ||||
| 			res := []string{} | ||||
| 			mut res := []string{} | ||||
| 			res << "'" | ||||
| 			for i, val in it.vals { | ||||
| 				res << val | ||||
|  |  | |||
|  | @ -17,12 +17,12 @@ import v.depgraph | |||
| 
 | ||||
| pub struct Builder { | ||||
| pub: | ||||
| 	pref                &pref.Preferences | ||||
| 	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: | ||||
| 	pref                &pref.Preferences | ||||
| 	module_search_paths []string | ||||
| 	parsed_files        []ast.File | ||||
| 	global_scope        &ast.Scope | ||||
|  |  | |||
|  | @ -364,11 +364,7 @@ pub fn (mut c Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type { | |||
| 		.left_shift { | ||||
| 			if left.kind == .array { | ||||
| 				// `array << elm`
 | ||||
| 				match infix_expr.left { | ||||
| 					ast.Ident {} | ||||
| 					ast.SelectorExpr {} | ||||
| 					else { println('typeof: ${typeof(infix_expr.left)}') } | ||||
| 				} | ||||
| 				c.fail_if_immutable(infix_expr.left) | ||||
| 				// the expressions have different types (array_x and x)
 | ||||
| 				if c.table.check(c.table.value_type(left_type), right_type) { | ||||
| 					// []T << T
 | ||||
|  | @ -446,6 +442,58 @@ pub fn (mut c Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut c Checker) fail_if_immutable(expr ast.Expr) { | ||||
| 	match expr { | ||||
| 		ast.Ident { | ||||
| 			scope := c.file.scope.innermost(expr.position().pos) | ||||
| 			if v := scope.find_var(it.name) { | ||||
| 				if !v.is_mut && !v.typ.is_ptr() { | ||||
| 					c.error('`$it.name` is immutable, declare it with `mut` to make it mutable', | ||||
| 						it.pos) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		ast.IndexExpr { | ||||
| 			c.fail_if_immutable(it.left) | ||||
| 		} | ||||
| 		ast.ParExpr { | ||||
| 			c.fail_if_immutable(it.expr) | ||||
| 		} | ||||
| 		ast.PrefixExpr { | ||||
| 			c.fail_if_immutable(it.right) | ||||
| 		} | ||||
| 		ast.SelectorExpr { | ||||
| 			// retrieve table.Field
 | ||||
| 			typ_sym := c.table.get_type_symbol(it.expr_type) | ||||
| 			match typ_sym.kind { | ||||
| 				.struct_ { | ||||
| 					struct_info := typ_sym.info as table.Struct | ||||
| 					field_info := struct_info.get_field(it.field) | ||||
| 					if !field_info.is_mut { | ||||
| 						type_str := c.table.type_to_str(it.expr_type) | ||||
| 						c.error('field `$it.field` of struct `${type_str}` is immutable', it.pos) | ||||
| 					} | ||||
| 					c.fail_if_immutable(it.expr) | ||||
| 				} | ||||
| 				.array, .string { | ||||
| 					// This should only happen in `builtin`
 | ||||
| 					// TODO Remove `crypto.rand` when possible (see vlib/crypto/rand/rand.v,
 | ||||
| 					// if `c_array_to_bytes_tmp` doesn't exist, then it's safe to remove it)
 | ||||
| 					if c.file.mod.name !in ['builtin', 'crypto.rand'] { | ||||
| 						c.error('`$typ_sym.kind` can not be modified', expr.position()) | ||||
| 					} | ||||
| 				} | ||||
| 				else { | ||||
| 					c.error('unexpected symbol `${typ_sym.kind}`', expr.position()) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		else { | ||||
| 			c.error('unexpected expression `${typeof(expr)}`', expr.position()) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn (mut c Checker) assign_expr(assign_expr mut ast.AssignExpr) { | ||||
| 	c.expected_type = table.void_type | ||||
| 	left_type := c.expr(assign_expr.left) | ||||
|  | @ -460,32 +508,7 @@ fn (mut c Checker) assign_expr(assign_expr mut ast.AssignExpr) { | |||
| 		return | ||||
| 	} | ||||
| 	// Make sure the variable is mutable
 | ||||
| 	match assign_expr.left { | ||||
| 		ast.Ident { | ||||
| 			scope := c.file.scope.innermost(assign_expr.pos.pos) | ||||
| 			if v := scope.find_var(it.name) { | ||||
| 				if !v.is_mut { | ||||
| 					c.error('`$it.name` is immutable, declare it with `mut` to assign to it', | ||||
| 						assign_expr.pos) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		ast.IndexExpr { | ||||
| 			// `m[key] = val`
 | ||||
| 			if it.left is ast.Ident { | ||||
| 				ident := it.left as ast.Ident | ||||
| 				// TODO copy pasta
 | ||||
| 				scope := c.file.scope.innermost(assign_expr.pos.pos) | ||||
| 				if v := scope.find_var(ident.name) { | ||||
| 					if !v.is_mut { | ||||
| 						c.error('`$ident.name` is immutable, declare it with `mut` to assign to it', | ||||
| 							assign_expr.pos) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		else {} | ||||
| 	} | ||||
| 	c.fail_if_immutable(assign_expr.left) | ||||
| 	// Single side check
 | ||||
| 	match assign_expr.op { | ||||
| 		.assign {} // No need to do single side check for =. But here put it first for speed.
 | ||||
|  |  | |||
|  | @ -0,0 +1,6 @@ | |||
| vlib/v/checker/tests/immutable_array_field_assign.v:9:4: error: field `i` of struct `A` is immutable | ||||
|     7|         i: [0] | ||||
|     8|     } | ||||
|     9|     a.i[0] = 3 | ||||
|              ^ | ||||
|    10| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| struct A { | ||||
| 	i []int | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	mut a := A{ | ||||
| 		i: [0] | ||||
| 	} | ||||
| 	a.i[0] = 3 | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| struct A { | ||||
| 	i []int | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	mut a := A{ | ||||
| 		i: [0] | ||||
| 	} | ||||
| 	a.i[0] = 3 | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| vlib/v/checker/tests/immutable_array_field_shift.v:14:4: error: field `a` of struct `B` is immutable | ||||
|    12|         a: A{} | ||||
|    13|     } | ||||
|    14|     b.a.i << 3 | ||||
|              ^ | ||||
|    15| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| struct A { | ||||
| mut: | ||||
| 	i []int | ||||
| } | ||||
| 
 | ||||
| struct B { | ||||
| 	a A | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	mut b := B{ | ||||
| 		a: A{} | ||||
| 	} | ||||
| 	b.a.i << 3 | ||||
| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| struct A { | ||||
| mut: | ||||
| 	i []int | ||||
| } | ||||
| 
 | ||||
| struct B { | ||||
| 	a A | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	mut b := B{ | ||||
| 		a: A{} | ||||
| 	} | ||||
| 	b.a.i << 3 | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| vlib/v/checker/tests/immutable_array_struct_assign.v:8:2: error: `a` is immutable, declare it with `mut` to make it mutable | ||||
|     6| fn main() { | ||||
|     7|     a := A{} | ||||
|     8|     a.i[0] += 3 | ||||
|            ^ | ||||
|     9| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| struct A { | ||||
| pub mut: | ||||
| 	i []int | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	a := A{} | ||||
| 	a.i[0] += 3 | ||||
| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| struct A { | ||||
| pub mut: | ||||
| 	i []int | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	a := A{} | ||||
| 	a.i[0] += 3 | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| vlib/v/checker/tests/immutable_array_struct_shift.v:8:2: error: `a` is immutable, declare it with `mut` to make it mutable | ||||
|     6| fn main() { | ||||
|     7|     a := []A{} | ||||
|     8|     a[0].i << 3 | ||||
|            ^ | ||||
|     9| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| struct A { | ||||
| pub mut: | ||||
| 	i []int | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	a := []A{} | ||||
| 	a[0].i << 3 | ||||
| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| struct A { | ||||
| pub mut: | ||||
| 	i []int | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	a := []A{} | ||||
| 	a[0].i << 3 | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| vlib/v/checker/tests/immutable_array_var.v:3:2: error: `a` is immutable, declare it with `mut` to make it mutable | ||||
|     1| fn main() { | ||||
|     2|     a := [1, 2] | ||||
|     3|     a << 3 | ||||
|            ^ | ||||
|     4| } | ||||
|  | @ -0,0 +1,4 @@ | |||
| fn main() { | ||||
| 	a := [1, 2] | ||||
| 	a << 3 | ||||
| } | ||||
|  | @ -0,0 +1,4 @@ | |||
| fn main() { | ||||
| 	a := [1, 2] | ||||
| 	a << 3 | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| vlib/v/checker/tests/immutable_field.v:8:4: error: field `i1` of struct `A` is immutable | ||||
|     6| fn main() { | ||||
|     7|     a := A{1} | ||||
|     8|     a.i1 = 2 | ||||
|              ~~ | ||||
|     9| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| struct A { | ||||
| pub: | ||||
| 	i1 int | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	a := A{1} | ||||
| 	a.i1 = 2 | ||||
| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| struct A { | ||||
| pub: | ||||
| 	i1 int | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	a := A{1} | ||||
| 	a.i1 = 2 | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| vlib/v/checker/tests/immutable_var.v:3:2: error: `a` is immutable, declare it with `mut` to make it mutable | ||||
|     1| fn main() { | ||||
|     2|     a := 1 | ||||
|     3|     a = 2 | ||||
|            ^ | ||||
|     4| } | ||||
|  | @ -0,0 +1,4 @@ | |||
| fn main() { | ||||
| 	a := 1 | ||||
| 	a = 2 | ||||
| } | ||||
|  | @ -0,0 +1,4 @@ | |||
| fn main() { | ||||
| 	a := 1 | ||||
| 	a = 2 | ||||
| } | ||||
|  | @ -1,6 +1,6 @@ | |||
| vlib/v/checker/tests/left_shift_err.v:3:7: error: cannot shift type string into array_int | ||||
|     1| fn main() { | ||||
|     2|     l := []int{} | ||||
|     2|     mut l := []int{} | ||||
|     3|     l << 'test' | ||||
|                 ~~~~~~ | ||||
|     4| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| fn main() { | ||||
| 	l := []int{} | ||||
| 	mut l := []int{} | ||||
| 	l << 'test' | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| fn main() { | ||||
| 	l := []int{} | ||||
| 	mut l := []int{} | ||||
| 	l << 'test' | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,6 @@ | |||
| vlib/v/checker/tests/struct_pub_field.v:9:4: error: field `i` of struct `A` is immutable | ||||
|     7|         i: 1 | ||||
|     8|     } | ||||
|     9|     a.i = 2 | ||||
|              ^ | ||||
|    10| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| struct A { | ||||
| 	i int | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	a := A{ | ||||
| 		i: 1 | ||||
| 	} | ||||
| 	a.i = 2 | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| struct A { | ||||
| 	i int | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
| 	a := A{ | ||||
| 		i: 1 | ||||
| 	} | ||||
| 	a.i = 2 | ||||
| } | ||||
|  | @ -56,7 +56,6 @@ struct Gen { | |||
| 	auto_str_funcs       strings.Builder // function bodies of all auto generated _str funcs
 | ||||
| 	comptime_defines     strings.Builder // custom defines, given by -d/-define flags on the CLI
 | ||||
| 	pcs_declarations     strings.Builder // -prof profile counter declarations for each function
 | ||||
| 	pcs                  []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
 | ||||
| 	table                &table.Table | ||||
| 	pref                 &pref.Preferences | ||||
| mut: | ||||
|  | @ -86,6 +85,7 @@ mut: | |||
| 	threaded_fns         []string // for generating unique wrapper types and fns for `go xxx()`
 | ||||
| 	array_fn_definitions []string // array equality functions that have been defined
 | ||||
| 	is_json_fn           bool // inside json.encode()
 | ||||
| 	pcs                  []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
 | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
|  |  | |||
|  | @ -16,15 +16,15 @@ const ( | |||
| ) | ||||
| 
 | ||||
| struct JsGen { | ||||
| 	out   			strings.Builder | ||||
| 	table           &table.Table | ||||
| 	definitions     strings.Builder | ||||
| 	pref            &pref.Preferences | ||||
| mut: | ||||
| 	out             strings.Builder | ||||
| 	namespaces		map[string]strings.Builder | ||||
| 	namespaces_pub	map[string][]string | ||||
| 	namespace 		string | ||||
| 	table 			&table.Table | ||||
| 	definitions 	strings.Builder | ||||
| 	pref            &pref.Preferences | ||||
| 	namespace       string | ||||
| 	doc				&JsDoc | ||||
| 	mut: | ||||
| 	constants		strings.Builder // all global V constants
 | ||||
| 	file			ast.File | ||||
| 	tmp_count		int | ||||
|  | @ -110,7 +110,7 @@ pub fn (g mut JsGen) escape_namespace() { | |||
| 
 | ||||
| pub fn (g mut JsGen) push_pub_var(s string) { | ||||
| 	// Workaround until `m[key]<<val` works.
 | ||||
| 	arr := g.namespaces_pub[g.namespace] | ||||
| 	mut arr := g.namespaces_pub[g.namespace] | ||||
| 	arr << s | ||||
| 	g.namespaces_pub[g.namespace] = arr | ||||
| } | ||||
|  | @ -123,7 +123,7 @@ pub fn (g mut JsGen) find_class_methods(stmts []ast.Stmt) { | |||
| 					// Found struct method, store it to be generated along with the class.
 | ||||
| 					class_name :=  g.table.get_type_symbol(it.receiver.typ).name | ||||
| 					// Workaround until `map[key] << val` works.
 | ||||
| 					arr := g.method_fn_decls[class_name] | ||||
| 					mut arr := g.method_fn_decls[class_name] | ||||
| 					arr << stmt | ||||
| 					g.method_fn_decls[class_name] = arr | ||||
| 				} | ||||
|  |  | |||
|  | @ -26,7 +26,6 @@ fn (mut g Gen) profile_fn(fn_name string, is_main bool){ | |||
| 	} | ||||
| } | ||||
| 
 | ||||
|          | ||||
| pub fn (mut g Gen) gen_vprint_profile_stats() { | ||||
| 	g.pcs_declarations.writeln('void vprint_profile_stats(){') | ||||
| 	fstring := '"%14llu %14.3fms %14.0fns %s \\n"' | ||||
|  |  | |||
|  | @ -284,7 +284,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn { | |||
| 		stmts = p.parse_block_no_scope() | ||||
| 	} | ||||
| 	p.close_scope() | ||||
| 	func := table.Fn{ | ||||
| 	mut func := table.Fn{ | ||||
| 		args: args | ||||
| 		is_variadic: is_variadic | ||||
| 		return_type: return_type | ||||
|  |  | |||
|  | @ -109,21 +109,16 @@ fn (mut p Parser) match_expr() ast.MatchExpr { | |||
| 		} else if p.tok.kind == .name && (p.tok.lit in table.builtin_type_names || (p.tok.lit[0].is_capital() && | ||||
| 			!p.tok.lit.is_upper()) || p.peek_tok.kind == .dot) { | ||||
| 			// Sum type match
 | ||||
| 			// if sym.kind == .sum_type {
 | ||||
| 			// p.warn('is sum')
 | ||||
| 			// TODO `exprs << ast.Type{...}
 | ||||
| 			typ := p.parse_type() | ||||
| 			x := ast.Type{ | ||||
| 			exprs << ast.Type{ | ||||
| 				typ: typ | ||||
| 			} | ||||
| 			mut expr := ast.Expr{} | ||||
| 			expr = x | ||||
| 			exprs << expr | ||||
| 			p.scope.register('it', ast.Var{ | ||||
| 				name: 'it' | ||||
| 				typ: typ.to_ptr() | ||||
| 				pos: cond_pos | ||||
| 				is_used: true | ||||
| 				is_mut: is_mut | ||||
| 			}) | ||||
| 			// TODO
 | ||||
| 			if p.tok.kind == .comma { | ||||
|  |  | |||
|  | @ -565,7 +565,7 @@ pub fn (mut p Parser) parse_ident(is_c, is_js bool) ast.Ident { | |||
| 	if p.expr_mod.len > 0 { | ||||
| 		name = '${p.expr_mod}.$name' | ||||
| 	} | ||||
| 	mut ident := ast.Ident{ | ||||
| 	return ast.Ident{ | ||||
| 		kind: .unresolved | ||||
| 		name: name | ||||
| 		is_c: is_c | ||||
|  | @ -573,7 +573,6 @@ pub fn (mut p Parser) parse_ident(is_c, is_js bool) ast.Ident { | |||
| 		mod: p.mod | ||||
| 		pos: pos | ||||
| 	} | ||||
| 	return ident | ||||
| } | ||||
| 
 | ||||
| pub fn (mut p Parser) name_expr() ast.Expr { | ||||
|  | @ -694,9 +693,7 @@ pub fn (mut p Parser) name_expr() ast.Expr { | |||
| 			mod: mod | ||||
| 		} | ||||
| 	} else { | ||||
| 		mut ident := ast.Ident{} | ||||
| 		ident = p.parse_ident(is_c, is_js) | ||||
| 		node = ident | ||||
| 		node = p.parse_ident(is_c, is_js) | ||||
| 	} | ||||
| 	p.expr_mod = '' | ||||
| 	return node | ||||
|  |  | |||
|  | @ -193,14 +193,12 @@ fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { | |||
| 		p.inside_is = true | ||||
| 	} | ||||
| 	right = p.expr(precedence) | ||||
| 	mut expr := ast.Expr{} | ||||
| 	expr = ast.InfixExpr{ | ||||
| 	return ast.InfixExpr{ | ||||
| 		left: left | ||||
| 		right: right | ||||
| 		op: op | ||||
| 		pos: pos | ||||
| 	} | ||||
| 	return expr | ||||
| } | ||||
| 
 | ||||
| fn (mut p Parser) prefix_expr() ast.PrefixExpr { | ||||
|  |  | |||
|  | @ -38,6 +38,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl { | |||
| 	mut mut_pos := -1 | ||||
| 	mut pub_pos := -1 | ||||
| 	mut pub_mut_pos := -1 | ||||
| 	mut is_field_mut := false | ||||
| 	mut is_field_pub := false | ||||
| 	mut is_field_global := false | ||||
| 	if !no_body { | ||||
| 		p.check(.lcbr) | ||||
| 		for p.tok.kind != .rcbr { | ||||
|  | @ -50,17 +53,29 @@ fn (mut p Parser) struct_decl() ast.StructDecl { | |||
| 				if p.tok.kind == .key_mut { | ||||
| 					p.check(.key_mut) | ||||
| 					pub_mut_pos = fields.len | ||||
| 					is_field_pub = true | ||||
| 					is_field_mut = true | ||||
| 					is_field_global = false | ||||
| 				} else { | ||||
| 					pub_pos = fields.len | ||||
| 					is_field_pub = true | ||||
| 					is_field_mut = false | ||||
| 					is_field_global = false | ||||
| 				} | ||||
| 				p.check(.colon) | ||||
| 			} else if p.tok.kind == .key_mut { | ||||
| 				p.check(.key_mut) | ||||
| 				p.check(.colon) | ||||
| 				mut_pos = fields.len | ||||
| 				is_field_pub = false | ||||
| 				is_field_mut = true | ||||
| 				is_field_global = false | ||||
| 			} else if p.tok.kind == .key_global { | ||||
| 				p.check(.key_global) | ||||
| 				p.check(.colon) | ||||
| 				is_field_pub = true | ||||
| 				is_field_mut = true | ||||
| 				is_field_global = true | ||||
| 			} | ||||
| 			field_name := p.check_name() | ||||
| 			field_pos := p.tok.position() | ||||
|  | @ -108,6 +123,9 @@ fn (mut p Parser) struct_decl() ast.StructDecl { | |||
| 				typ: typ | ||||
| 				default_expr: default_expr | ||||
| 				has_default_expr: has_default_expr | ||||
| 				is_pub: is_field_pub | ||||
| 				is_mut: is_field_mut | ||||
| 				is_global: is_field_global | ||||
| 			} | ||||
| 			// println('struct field $ti.name $field_name')
 | ||||
| 		} | ||||
|  |  | |||
|  | @ -311,6 +311,14 @@ pub fn (t &TypeSymbol) map_info() Map { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| pub fn (t &TypeSymbol) struct_info() Struct { | ||||
| 	match t.info { | ||||
| 		Struct { return it } | ||||
| 		else { panic('TypeSymbol.struct_info(): no struct info for type: $t.name') } | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| pub fn (t TypeSymbol) str() string { | ||||
| 	return t.name | ||||
|  | @ -499,6 +507,7 @@ pub mut: | |||
| } | ||||
| 
 | ||||
| pub struct Interface { | ||||
| mut: | ||||
| 	gen_types []string | ||||
| 	foo       string | ||||
| } | ||||
|  | @ -522,6 +531,9 @@ mut: | |||
| 	has_default_expr bool | ||||
| 	default_val      string | ||||
| 	attr             string | ||||
| 	is_pub           bool | ||||
| 	is_mut           bool | ||||
| 	is_global        bool | ||||
| } | ||||
| 
 | ||||
| pub struct Array { | ||||
|  | @ -594,3 +606,19 @@ pub fn (table &Table) type_to_str(t Type) string { | |||
| 	*/ | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| pub fn (s Struct) find_field(name string) ?Field { | ||||
| 	for field in s.fields { | ||||
| 		if field.name == name { | ||||
| 			return field | ||||
| 		} | ||||
| 	} | ||||
| 	return none | ||||
| } | ||||
| 
 | ||||
| pub fn (s Struct) get_field(name string) Field { | ||||
| 	if field := s.find_field(name) { | ||||
| 		return field | ||||
| 	} | ||||
| 	panic('unknown field `$name`') | ||||
| } | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ import v.cflag | |||
| 
 | ||||
| const ( | ||||
| 	module_name = 'main' | ||||
| 	cdefines    = []string | ||||
| 	cdefines    = []string{} | ||||
| 	no_name     = '' | ||||
| 	no_flag     = '' | ||||
| 	no_os       = '' | ||||
|  |  | |||
|  | @ -18,7 +18,6 @@ pub mut: | |||
| 
 | ||||
| pub struct Fn { | ||||
| pub: | ||||
| 	name        string | ||||
| 	args        []Arg | ||||
| 	return_type Type | ||||
| 	is_variadic bool | ||||
|  | @ -28,6 +27,8 @@ pub: | |||
| 	is_pub      bool | ||||
| 	mod         string | ||||
| 	ctdefine    string // compile time define. myflag, when [if myflag] tag
 | ||||
| pub mut: | ||||
| 	name        string | ||||
| } | ||||
| 
 | ||||
| pub struct Arg { | ||||
|  | @ -486,7 +487,7 @@ pub fn (t &Table) check(got, expected Type) bool { | |||
| 	exp_type_sym := t.get_type_symbol(expected) | ||||
| 	//
 | ||||
| 	if exp_type_sym.kind == .interface_ { | ||||
| 		info := exp_type_sym.info as Interface | ||||
| 		mut info := exp_type_sym.info as Interface | ||||
| 		// println('gen_types before')
 | ||||
| 		// println(info.gen_types)
 | ||||
| 		info.gen_types << got_type_sym.name | ||||
|  |  | |||
|  | @ -40,5 +40,5 @@ pub fn cescaped_path(s string) string { | |||
| } | ||||
| 
 | ||||
| pub fn is_fmt() bool { | ||||
| 	return os.executable().contains('vfmt')     | ||||
| 	return os.executable().contains('vfmt') | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue