v.parser, v.checker, v.gen: add support for [translated] tag (#13373)
							parent
							
								
									054c8b1f13
								
							
						
					
					
						commit
						cec7e91714
					
				|  | @ -668,12 +668,13 @@ pub mut: | ||||||
| [heap] | [heap] | ||||||
| pub struct File { | pub struct File { | ||||||
| pub: | pub: | ||||||
| 	nr_lines     int    // number of source code lines in the file (including newlines and comments)
 | 	nr_lines      int    // number of source code lines in the file (including newlines and comments)
 | ||||||
| 	nr_bytes     int    // number of processed source code bytes
 | 	nr_bytes      int    // number of processed source code bytes
 | ||||||
| 	mod          Module // the module of the source file (from `module xyz` at the top)
 | 	mod           Module // the module of the source file (from `module xyz` at the top)
 | ||||||
| 	global_scope &Scope | 	global_scope  &Scope | ||||||
| 	is_test      bool // true for _test.v files
 | 	is_test       bool // true for _test.v files
 | ||||||
| 	is_generated bool // true for `[generated] module xyz` files; turn off notices
 | 	is_generated  bool // true for `[generated] module xyz` files; turn off notices
 | ||||||
|  | 	is_translated bool // true for `[translated] module xyz` files; turn off some checks
 | ||||||
| pub mut: | pub mut: | ||||||
| 	path             string // absolute path of the source file - '/projects/v/file.v'
 | 	path             string // absolute path of the source file - '/projects/v/file.v'
 | ||||||
| 	path_base        string // file name - 'file.v' (useful for tracing)
 | 	path_base        string // file name - 'file.v' (useful for tracing)
 | ||||||
|  |  | ||||||
|  | @ -177,7 +177,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { | ||||||
| 					} | 					} | ||||||
| 					if obj.is_stack_obj && !c.inside_unsafe { | 					if obj.is_stack_obj && !c.inside_unsafe { | ||||||
| 						type_sym := c.table.sym(obj.typ.set_nr_muls(0)) | 						type_sym := c.table.sym(obj.typ.set_nr_muls(0)) | ||||||
| 						if !type_sym.is_heap() && !c.pref.translated { | 						if !type_sym.is_heap() && !c.pref.translated && !c.file.is_translated { | ||||||
| 							suggestion := if type_sym.kind == .struct_ { | 							suggestion := if type_sym.kind == .struct_ { | ||||||
| 								'declaring `$type_sym.name` as `[heap]`' | 								'declaring `$type_sym.name` as `[heap]`' | ||||||
| 							} else { | 							} else { | ||||||
|  | @ -278,7 +278,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { | ||||||
| 			ast.PrefixExpr { | 			ast.PrefixExpr { | ||||||
| 				// Do now allow `*x = y` outside `unsafe`
 | 				// Do now allow `*x = y` outside `unsafe`
 | ||||||
| 				if left.op == .mul { | 				if left.op == .mul { | ||||||
| 					if !c.inside_unsafe && !c.pref.translated { | 					if !c.inside_unsafe && !c.pref.translated && !c.file.is_translated { | ||||||
| 						c.error('modifying variables via dereferencing can only be done in `unsafe` blocks', | 						c.error('modifying variables via dereferencing can only be done in `unsafe` blocks', | ||||||
| 							node.pos) | 							node.pos) | ||||||
| 					} else { | 					} else { | ||||||
|  | @ -325,7 +325,7 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { | ||||||
| 			// right type was a generic `T`
 | 			// right type was a generic `T`
 | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		if c.pref.translated { | 		if c.pref.translated || c.file.is_translated { | ||||||
| 			// TODO fix this in C2V instead, for example cast enums to int before using `|` on them.
 | 			// TODO fix this in C2V instead, for example cast enums to int before using `|` on them.
 | ||||||
| 			// TODO replace all c.pref.translated checks with `$if !translated` for performance
 | 			// TODO replace all c.pref.translated checks with `$if !translated` for performance
 | ||||||
| 			continue | 			continue | ||||||
|  |  | ||||||
|  | @ -369,7 +369,7 @@ fn (mut c Checker) check_shift(mut node ast.InfixExpr, left_type ast.Type, right | ||||||
| 						ast.u64_type { 63 } | 						ast.u64_type { 63 } | ||||||
| 						else { 64 } | 						else { 64 } | ||||||
| 					} | 					} | ||||||
| 					if ival > moffset && !c.pref.translated { | 					if ival > moffset && !c.pref.translated && !c.file.is_translated { | ||||||
| 						c.error('shift count for type `$left_sym_final.name` too large (maximum: $moffset bits)', | 						c.error('shift count for type `$left_sym_final.name` too large (maximum: $moffset bits)', | ||||||
| 							node.right.pos()) | 							node.right.pos()) | ||||||
| 						return left_type | 						return left_type | ||||||
|  |  | ||||||
|  | @ -390,11 +390,13 @@ fn (mut c Checker) file_has_main_fn(file &ast.File) bool { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn (mut c Checker) check_valid_snake_case(name string, identifier string, pos token.Pos) { | fn (mut c Checker) check_valid_snake_case(name string, identifier string, pos token.Pos) { | ||||||
| 	if !c.pref.is_vweb && !c.pref.translated && name.len > 0 | 	if c.pref.translated || c.file.is_translated { | ||||||
| 		&& (name[0] == `_` || name.contains('._')) { | 		return | ||||||
|  | 	} | ||||||
|  | 	if !c.pref.is_vweb && name.len > 0 && (name[0] == `_` || name.contains('._')) { | ||||||
| 		c.error('$identifier `$name` cannot start with `_`', pos) | 		c.error('$identifier `$name` cannot start with `_`', pos) | ||||||
| 	} | 	} | ||||||
| 	if !c.pref.experimental && !c.pref.translated && util.contains_capital(name) { | 	if !c.pref.experimental && util.contains_capital(name) { | ||||||
| 		c.error('$identifier `$name` cannot contain uppercase letters, use snake_case instead', | 		c.error('$identifier `$name` cannot contain uppercase letters, use snake_case instead', | ||||||
| 			pos) | 			pos) | ||||||
| 	} | 	} | ||||||
|  | @ -407,7 +409,7 @@ fn stripped_name(name string) string { | ||||||
| 
 | 
 | ||||||
| fn (mut c Checker) check_valid_pascal_case(name string, identifier string, pos token.Pos) { | fn (mut c Checker) check_valid_pascal_case(name string, identifier string, pos token.Pos) { | ||||||
| 	sname := stripped_name(name) | 	sname := stripped_name(name) | ||||||
| 	if sname.len > 0 && !sname[0].is_capital() && !c.pref.translated { | 	if sname.len > 0 && !sname[0].is_capital() && !c.pref.translated && !c.file.is_translated { | ||||||
| 		c.error('$identifier `$name` must begin with capital letter', pos) | 		c.error('$identifier `$name` must begin with capital letter', pos) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -952,7 +954,7 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { | ||||||
| 			return ast.void_type | 			return ast.void_type | ||||||
| 		} | 		} | ||||||
| 		.and, .logical_or { | 		.and, .logical_or { | ||||||
| 			if !c.pref.translated { | 			if !c.pref.translated && !c.file.is_translated { | ||||||
| 				if node.left_type != ast.bool_type_idx { | 				if node.left_type != ast.bool_type_idx { | ||||||
| 					c.error('left operand for `$node.op` is not a boolean', node.left.pos()) | 					c.error('left operand for `$node.op` is not a boolean', node.left.pos()) | ||||||
| 				} | 				} | ||||||
|  | @ -987,7 +989,7 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { | ||||||
| 				c.error('only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed', | 				c.error('only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed', | ||||||
| 					node.pos) | 					node.pos) | ||||||
| 			} | 			} | ||||||
| 		} else if !c.pref.translated { | 		} else if !c.pref.translated && !c.file.is_translated { | ||||||
| 			// Regular enums
 | 			// Regular enums
 | ||||||
| 			c.error('only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed', | 			c.error('only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed', | ||||||
| 				node.pos) | 				node.pos) | ||||||
|  | @ -1008,7 +1010,8 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { | ||||||
| 	} | 	} | ||||||
| 	// Dual sides check (compatibility check)
 | 	// Dual sides check (compatibility check)
 | ||||||
| 	if !(c.symmetric_check(left_type, right_type) && c.symmetric_check(right_type, left_type)) | 	if !(c.symmetric_check(left_type, right_type) && c.symmetric_check(right_type, left_type)) | ||||||
| 		&& !c.pref.translated && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() { | 		&& !c.pref.translated && !c.file.is_translated && !node.left.is_auto_deref_var() | ||||||
|  | 		&& !node.right.is_auto_deref_var() { | ||||||
| 		// for type-unresolved consts
 | 		// for type-unresolved consts
 | ||||||
| 		if left_type == ast.void_type || right_type == ast.void_type { | 		if left_type == ast.void_type || right_type == ast.void_type { | ||||||
| 			return ast.void_type | 			return ast.void_type | ||||||
|  | @ -1047,7 +1050,7 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Pos) { | ||||||
| 		ast.Ident { | 		ast.Ident { | ||||||
| 			if expr.obj is ast.Var { | 			if expr.obj is ast.Var { | ||||||
| 				mut v := expr.obj as ast.Var | 				mut v := expr.obj as ast.Var | ||||||
| 				if !v.is_mut && !c.pref.translated && !c.inside_unsafe { | 				if !v.is_mut && !c.pref.translated && !c.file.is_translated && !c.inside_unsafe { | ||||||
| 					c.error('`$expr.name` is immutable, declare it with `mut` to make it mutable', | 					c.error('`$expr.name` is immutable, declare it with `mut` to make it mutable', | ||||||
| 						expr.pos) | 						expr.pos) | ||||||
| 				} | 				} | ||||||
|  | @ -1137,7 +1140,7 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Pos) { | ||||||
| 							pos = expr.pos | 							pos = expr.pos | ||||||
| 						} | 						} | ||||||
| 					} else { | 					} else { | ||||||
| 						if !field_info.is_mut && !c.pref.translated { | 						if !field_info.is_mut && !c.pref.translated && !c.file.is_translated { | ||||||
| 							type_str := c.table.type_to_str(expr.expr_type) | 							type_str := c.table.type_to_str(expr.expr_type) | ||||||
| 							c.error('field `$expr.field_name` of struct `$type_str` is immutable', | 							c.error('field `$expr.field_name` of struct `$type_str` is immutable', | ||||||
| 								expr.pos) | 								expr.pos) | ||||||
|  | @ -1696,7 +1699,8 @@ pub fn (mut c Checker) enum_decl(mut node ast.EnumDecl) { | ||||||
| 					val := field.expr.val.i64() | 					val := field.expr.val.i64() | ||||||
| 					if val < checker.int_min || val > checker.int_max { | 					if val < checker.int_min || val > checker.int_max { | ||||||
| 						c.error('enum value `$val` overflows int', field.expr.pos) | 						c.error('enum value `$val` overflows int', field.expr.pos) | ||||||
| 					} else if !c.pref.translated && !node.is_multi_allowed && i64(val) in seen { | 					} else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed | ||||||
|  | 						&& i64(val) in seen { | ||||||
| 						c.error('enum value `$val` already exists', field.expr.pos) | 						c.error('enum value `$val` already exists', field.expr.pos) | ||||||
| 					} | 					} | ||||||
| 					seen << i64(val) | 					seen << i64(val) | ||||||
|  | @ -1728,7 +1732,8 @@ pub fn (mut c Checker) enum_decl(mut node ast.EnumDecl) { | ||||||
| 				last := seen[seen.len - 1] | 				last := seen[seen.len - 1] | ||||||
| 				if last == checker.int_max { | 				if last == checker.int_max { | ||||||
| 					c.error('enum value overflows', field.pos) | 					c.error('enum value overflows', field.pos) | ||||||
| 				} else if !c.pref.translated && !node.is_multi_allowed && last + 1 in seen { | 				} else if !c.pref.translated && !c.file.is_translated && !node.is_multi_allowed | ||||||
|  | 					&& last + 1 in seen { | ||||||
| 					c.error('enum value `${last + 1}` already exists', field.pos) | 					c.error('enum value `${last + 1}` already exists', field.pos) | ||||||
| 				} | 				} | ||||||
| 				seen << last + 1 | 				seen << last + 1 | ||||||
|  | @ -2720,7 +2725,7 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} else if to_type == ast.bool_type && from_type != ast.bool_type && !c.inside_unsafe | 	} else if to_type == ast.bool_type && from_type != ast.bool_type && !c.inside_unsafe | ||||||
| 		&& !c.pref.translated { | 		&& !c.pref.translated && !c.file.is_translated { | ||||||
| 		c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos) | 		c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos) | ||||||
| 	} else if from_type == ast.none_type && !to_type.has_flag(.optional) { | 	} else if from_type == ast.none_type && !to_type.has_flag(.optional) { | ||||||
| 		type_name := c.table.type_to_str(to_type) | 		type_name := c.table.type_to_str(to_type) | ||||||
|  | @ -3404,21 +3409,16 @@ pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr, as_interface bool) | ||||||
| 					obj = c.fn_scope.find_var(node.obj.name) or { obj } | 					obj = c.fn_scope.find_var(node.obj.name) or { obj } | ||||||
| 				} | 				} | ||||||
| 				type_sym := c.table.sym(obj.typ.set_nr_muls(0)) | 				type_sym := c.table.sym(obj.typ.set_nr_muls(0)) | ||||||
| 				if obj.is_stack_obj && !type_sym.is_heap() && !c.pref.translated { | 				if obj.is_stack_obj && !type_sym.is_heap() && !c.pref.translated | ||||||
|  | 					&& !c.file.is_translated { | ||||||
| 					suggestion := if type_sym.kind == .struct_ { | 					suggestion := if type_sym.kind == .struct_ { | ||||||
| 						'declaring `$type_sym.name` as `[heap]`' | 						'declaring `$type_sym.name` as `[heap]`' | ||||||
| 					} else { | 					} else { | ||||||
| 						'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`' | 						'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`' | ||||||
| 					} | 					} | ||||||
| 					if !c.pref.translated { | 					mischief := if as_interface { 'used as interface object' } else { 'referenced' } | ||||||
| 						mischief := if as_interface { | 					c.error('`$node.name` cannot be $mischief outside `unsafe` blocks as it might be stored on stack. Consider ${suggestion}.', | ||||||
| 							'used as interface object' | 						node.pos) | ||||||
| 						} else { |  | ||||||
| 							'referenced' |  | ||||||
| 						} |  | ||||||
| 						c.error('`$node.name` cannot be $mischief outside `unsafe` blocks as it might be stored on stack. Consider ${suggestion}.', |  | ||||||
| 							node.pos) |  | ||||||
| 					} |  | ||||||
| 				} else if type_sym.kind == .array_fixed { | 				} else if type_sym.kind == .array_fixed { | ||||||
| 					c.error('cannot reference fixed array `$node.name` outside `unsafe` blocks as it is supposed to be stored on stack', | 					c.error('cannot reference fixed array `$node.name` outside `unsafe` blocks as it is supposed to be stored on stack', | ||||||
| 						node.pos) | 						node.pos) | ||||||
|  | @ -3532,15 +3532,16 @@ pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type { | ||||||
| 		if right_type.is_ptr() { | 		if right_type.is_ptr() { | ||||||
| 			return right_type.deref() | 			return right_type.deref() | ||||||
| 		} | 		} | ||||||
| 		if !right_type.is_pointer() && !c.pref.translated { | 		if !right_type.is_pointer() && !c.pref.translated && !c.file.is_translated { | ||||||
| 			s := c.table.type_to_str(right_type) | 			s := c.table.type_to_str(right_type) | ||||||
| 			c.error('invalid indirect of `$s`', node.pos) | 			c.error('invalid indirect of `$s`', node.pos) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if node.op == .bit_not && !right_type.is_int() && !c.pref.translated { | 	if node.op == .bit_not && !right_type.is_int() && !c.pref.translated && !c.file.is_translated { | ||||||
| 		c.error('operator ~ only defined on int types', node.pos) | 		c.error('operator ~ only defined on int types', node.pos) | ||||||
| 	} | 	} | ||||||
| 	if node.op == .not && right_type != ast.bool_type_idx && !c.pref.translated { | 	if node.op == .not && right_type != ast.bool_type_idx && !c.pref.translated | ||||||
|  | 		&& !c.file.is_translated { | ||||||
| 		c.error('! operator can only be used with bool types', node.pos) | 		c.error('! operator can only be used with bool types', node.pos) | ||||||
| 	} | 	} | ||||||
| 	// FIXME
 | 	// FIXME
 | ||||||
|  | @ -3654,7 +3655,7 @@ pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type { | ||||||
| 				is_ok = v.is_mut && v.is_arg && !typ.deref().is_ptr() | 				is_ok = v.is_mut && v.is_arg && !typ.deref().is_ptr() | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		if !is_ok && !c.pref.translated { | 		if !is_ok && !c.pref.translated && !c.file.is_translated { | ||||||
| 			c.warn('pointer indexing is only allowed in `unsafe` blocks', node.pos) | 			c.warn('pointer indexing is only allowed in `unsafe` blocks', node.pos) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -3736,7 +3737,7 @@ pub fn (mut c Checker) enum_val(mut node ast.EnumVal) ast.Type { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	mut typ := ast.new_type(typ_idx) | 	mut typ := ast.new_type(typ_idx) | ||||||
| 	if c.pref.translated { | 	if c.pref.translated || c.file.is_translated { | ||||||
| 		// TODO make more strict
 | 		// TODO make more strict
 | ||||||
| 		node.typ = typ | 		node.typ = typ | ||||||
| 		return typ | 		return typ | ||||||
|  | @ -3752,7 +3753,7 @@ pub fn (mut c Checker) enum_val(mut node ast.EnumVal) ast.Type { | ||||||
| 		typ_sym = c.table.sym(typ) | 		typ_sym = c.table.sym(typ) | ||||||
| 	} | 	} | ||||||
| 	fsym := c.table.final_sym(typ) | 	fsym := c.table.final_sym(typ) | ||||||
| 	if fsym.kind != .enum_ && !c.pref.translated { | 	if fsym.kind != .enum_ && !c.pref.translated && !c.file.is_translated { | ||||||
| 		// TODO in C int fields can be compared to enums, need to handle that in C2V
 | 		// TODO in C int fields can be compared to enums, need to handle that in C2V
 | ||||||
| 		c.error('expected type is not an enum (`$typ_sym.name`)', node.pos) | 		c.error('expected type is not an enum (`$typ_sym.name`)', node.pos) | ||||||
| 		return ast.void_type | 		return ast.void_type | ||||||
|  | @ -3839,7 +3840,7 @@ pub fn (mut c Checker) error(message string, pos token.Pos) { | ||||||
| 		print_backtrace() | 		print_backtrace() | ||||||
| 		exit(1) | 		exit(1) | ||||||
| 	} | 	} | ||||||
| 	if c.pref.translated && message.starts_with('mismatched types') { | 	if (c.pref.translated || c.file.is_translated) && message.starts_with('mismatched types') { | ||||||
| 		// TODO move this
 | 		// TODO move this
 | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -207,7 +207,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			if c.pref.translated && node.is_variadic && node.params.len == 1 && param.typ.is_ptr() { | 			if (c.pref.translated || c.file.is_translated) && node.is_variadic | ||||||
|  | 				&& node.params.len == 1 && param.typ.is_ptr() { | ||||||
| 				// TODO c2v hack to fix `(const char *s, ...)`
 | 				// TODO c2v hack to fix `(const char *s, ...)`
 | ||||||
| 				param.typ = ast.int_type.ref() | 				param.typ = ast.int_type.ref() | ||||||
| 			} | 			} | ||||||
|  | @ -679,7 +680,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) | ||||||
| 	} | 	} | ||||||
| 	node.is_keep_alive = func.is_keep_alive | 	node.is_keep_alive = func.is_keep_alive | ||||||
| 	if func.mod != 'builtin' && func.language == .v && func.no_body && !c.pref.translated | 	if func.mod != 'builtin' && func.language == .v && func.no_body && !c.pref.translated | ||||||
| 		&& !func.is_unsafe { | 		&& !c.file.is_translated && !func.is_unsafe { | ||||||
| 		c.error('cannot call a function that does not have a body', node.pos) | 		c.error('cannot call a function that does not have a body', node.pos) | ||||||
| 	} | 	} | ||||||
| 	for concrete_type in node.concrete_types { | 	for concrete_type in node.concrete_types { | ||||||
|  | @ -827,7 +828,8 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) | ||||||
| 		if call_arg.typ != param.typ | 		if call_arg.typ != param.typ | ||||||
| 			&& (param.typ == ast.voidptr_type || final_param_sym.idx == ast.voidptr_type_idx) | 			&& (param.typ == ast.voidptr_type || final_param_sym.idx == ast.voidptr_type_idx) | ||||||
| 			&& !call_arg.typ.is_any_kind_of_pointer() && func.language == .v | 			&& !call_arg.typ.is_any_kind_of_pointer() && func.language == .v | ||||||
| 			&& !call_arg.expr.is_lvalue() && func.name != 'json.encode' && !c.pref.translated { | 			&& !call_arg.expr.is_lvalue() && func.name != 'json.encode' && !c.pref.translated | ||||||
|  | 			&& !c.file.is_translated { | ||||||
| 			c.error('expression cannot be passed as `voidptr`', call_arg.expr.pos()) | 			c.error('expression cannot be passed as `voidptr`', call_arg.expr.pos()) | ||||||
| 		} | 		} | ||||||
| 		// Handle expected interface
 | 		// Handle expected interface
 | ||||||
|  | @ -853,7 +855,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) | ||||||
| 			if param.typ.has_flag(.generic) { | 			if param.typ.has_flag(.generic) { | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			if c.pref.translated { | 			if c.pref.translated || c.file.is_translated { | ||||||
| 				// TODO duplicated logic in check_types() (check_types.v)
 | 				// TODO duplicated logic in check_types() (check_types.v)
 | ||||||
| 				// Allow enums to be used as ints and vice versa in translated code
 | 				// Allow enums to be used as ints and vice versa in translated code
 | ||||||
| 				if param.typ == ast.int_type && typ_sym.kind == .enum_ { | 				if param.typ == ast.int_type && typ_sym.kind == .enum_ { | ||||||
|  |  | ||||||
|  | @ -144,7 +144,7 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) { | ||||||
| 	prev_loop_label := c.loop_label | 	prev_loop_label := c.loop_label | ||||||
| 	c.expected_type = ast.bool_type | 	c.expected_type = ast.bool_type | ||||||
| 	typ := c.expr(node.cond) | 	typ := c.expr(node.cond) | ||||||
| 	if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated { | 	if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated && !c.file.is_translated { | ||||||
| 		c.error('non-bool used as for condition', node.pos) | 		c.error('non-bool used as for condition', node.pos) | ||||||
| 	} | 	} | ||||||
| 	if mut node.cond is ast.InfixExpr { | 	if mut node.cond is ast.InfixExpr { | ||||||
|  |  | ||||||
|  | @ -28,7 +28,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { | ||||||
| 	mut is_comptime_type_is_expr := false // if `$if T is string`
 | 	mut is_comptime_type_is_expr := false // if `$if T is string`
 | ||||||
| 	for i in 0 .. node.branches.len { | 	for i in 0 .. node.branches.len { | ||||||
| 		mut branch := node.branches[i] | 		mut branch := node.branches[i] | ||||||
| 		if branch.cond is ast.ParExpr && !c.pref.translated { | 		if branch.cond is ast.ParExpr && !c.pref.translated && !c.file.is_translated { | ||||||
| 			c.error('unnecessary `()` in `$if_kind` condition, use `$if_kind expr {` instead of `$if_kind (expr) {`.', | 			c.error('unnecessary `()` in `$if_kind` condition, use `$if_kind expr {` instead of `$if_kind (expr) {`.', | ||||||
| 				branch.pos) | 				branch.pos) | ||||||
| 		} | 		} | ||||||
|  | @ -41,7 +41,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { | ||||||
| 				c.expected_type = ast.bool_type | 				c.expected_type = ast.bool_type | ||||||
| 				cond_typ := c.unwrap_generic(c.expr(branch.cond)) | 				cond_typ := c.unwrap_generic(c.expr(branch.cond)) | ||||||
| 				if (cond_typ.idx() != ast.bool_type_idx || cond_typ.has_flag(.optional)) | 				if (cond_typ.idx() != ast.bool_type_idx || cond_typ.has_flag(.optional)) | ||||||
| 					&& !c.pref.translated { | 					&& !c.pref.translated && !c.file.is_translated { | ||||||
| 					c.error('non-bool type `${c.table.type_to_str(cond_typ)}` used as if condition', | 					c.error('non-bool type `${c.table.type_to_str(cond_typ)}` used as if condition', | ||||||
| 						branch.cond.pos()) | 						branch.cond.pos()) | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ import strings | ||||||
| pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { | pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { | ||||||
| 	node.is_expr = c.expected_type != ast.void_type | 	node.is_expr = c.expected_type != ast.void_type | ||||||
| 	node.expected_type = c.expected_type | 	node.expected_type = c.expected_type | ||||||
| 	if mut node.cond is ast.ParExpr && !c.pref.translated { | 	if mut node.cond is ast.ParExpr && !c.pref.translated && !c.file.is_translated { | ||||||
| 		c.error('unnecessary `()` in `match` condition, use `match expr {` instead of `match (expr) {`.', | 		c.error('unnecessary `()` in `match` condition, use `match expr {` instead of `match (expr) {`.', | ||||||
| 			node.cond.pos) | 			node.cond.pos) | ||||||
| 	} | 	} | ||||||
|  | @ -333,7 +333,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if is_exhaustive { | 	if is_exhaustive { | ||||||
| 		if has_else && !c.pref.translated { | 		if has_else && !c.pref.translated && !c.file.is_translated { | ||||||
| 			c.error('match expression is exhaustive, `else` is unnecessary', else_branch.pos) | 			c.error('match expression is exhaustive, `else` is unnecessary', else_branch.pos) | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
|  |  | ||||||
|  | @ -113,7 +113,7 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) { | ||||||
| 		} | 		} | ||||||
| 		if (exp_type.is_ptr() || exp_type.is_pointer()) | 		if (exp_type.is_ptr() || exp_type.is_pointer()) | ||||||
| 			&& (!got_typ.is_ptr() && !got_typ.is_pointer()) && got_typ != ast.int_literal_type | 			&& (!got_typ.is_ptr() && !got_typ.is_pointer()) && got_typ != ast.int_literal_type | ||||||
| 			&& !c.pref.translated { | 			&& !c.pref.translated && !c.file.is_translated { | ||||||
| 			pos := node.exprs[i].pos() | 			pos := node.exprs[i].pos() | ||||||
| 			if node.exprs[i].is_auto_deref_var() { | 			if node.exprs[i].is_auto_deref_var() { | ||||||
| 				continue | 				continue | ||||||
|  | @ -131,7 +131,7 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) { | ||||||
| 					} | 					} | ||||||
| 					if obj.is_stack_obj && !c.inside_unsafe { | 					if obj.is_stack_obj && !c.inside_unsafe { | ||||||
| 						type_sym := c.table.sym(obj.typ.set_nr_muls(0)) | 						type_sym := c.table.sym(obj.typ.set_nr_muls(0)) | ||||||
| 						if !type_sym.is_heap() && !c.pref.translated { | 						if !type_sym.is_heap() && !c.pref.translated && !c.file.is_translated { | ||||||
| 							suggestion := if type_sym.kind == .struct_ { | 							suggestion := if type_sym.kind == .struct_ { | ||||||
| 								'declaring `$type_sym.name` as `[heap]`' | 								'declaring `$type_sym.name` as `[heap]`' | ||||||
| 							} else { | 							} else { | ||||||
|  |  | ||||||
|  | @ -313,7 +313,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { | ||||||
| 							} | 							} | ||||||
| 							if obj.is_stack_obj && !c.inside_unsafe { | 							if obj.is_stack_obj && !c.inside_unsafe { | ||||||
| 								sym := c.table.sym(obj.typ.set_nr_muls(0)) | 								sym := c.table.sym(obj.typ.set_nr_muls(0)) | ||||||
| 								if !sym.is_heap() && !c.pref.translated { | 								if !sym.is_heap() && !c.pref.translated && !c.file.is_translated { | ||||||
| 									suggestion := if sym.kind == .struct_ { | 									suggestion := if sym.kind == .struct_ { | ||||||
| 										'declaring `$sym.name` as `[heap]`' | 										'declaring `$sym.name` as `[heap]`' | ||||||
| 									} else { | 									} else { | ||||||
|  | @ -348,7 +348,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { | ||||||
| 					continue | 					continue | ||||||
| 				} | 				} | ||||||
| 				if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr | 				if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr | ||||||
| 					&& !c.pref.translated { | 					&& !c.pref.translated && !c.file.is_translated { | ||||||
| 					c.error('reference field `${type_sym.name}.$field.name` must be initialized', | 					c.error('reference field `${type_sym.name}.$field.name` must be initialized', | ||||||
| 						node.pos) | 						node.pos) | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| vlib/v/checker/tests/static_vars_in_translated_mode.vv:2:13: error: static variables are supported only in -translated mode or in [unsafe] fn | vlib/v/checker/tests/static_vars_in_translated_mode.vv:2:13: error: static variables are supported only in translated mode or in [unsafe] fn | ||||||
|     1 | fn counter() int { |     1 | fn counter() int { | ||||||
|     2 |     mut static icounter := 0 |     2 |     mut static icounter := 0 | ||||||
|       |                ~~~~~~~~ |       |                ~~~~~~~~ | ||||||
|  |  | ||||||
|  | @ -4474,7 +4474,7 @@ fn (mut g Gen) ident(node ast.Ident) { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} else if node_info is ast.IdentFn { | 	} else if node_info is ast.IdentFn { | ||||||
| 		if g.pref.translated { | 		if g.pref.translated || g.file.is_translated { | ||||||
| 			// `p_mobjthinker` => `P_MobjThinker`
 | 			// `p_mobjthinker` => `P_MobjThinker`
 | ||||||
| 			if f := g.table.find_fn(node.name) { | 			if f := g.table.find_fn(node.name) { | ||||||
| 				// TODO PERF fn lookup for each fn call in translated mode
 | 				// TODO PERF fn lookup for each fn call in translated mode
 | ||||||
|  | @ -4511,7 +4511,7 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) { | ||||||
| 		g.expr(node.expr) | 		g.expr(node.expr) | ||||||
| 	} else { | 	} else { | ||||||
| 		styp := g.typ(node.typ) | 		styp := g.typ(node.typ) | ||||||
| 		if g.pref.translated && sym.kind == .function { | 		if (g.pref.translated || g.file.is_translated) && sym.kind == .function { | ||||||
| 			// TODO handle the type in fn casts, not just exprs
 | 			// TODO handle the type in fn casts, not just exprs
 | ||||||
| 			/* | 			/* | ||||||
| 			info := sym.info as ast.FnType | 			info := sym.info as ast.FnType | ||||||
|  |  | ||||||
|  | @ -436,7 +436,7 @@ fn (mut g Gen) c_fn_name(node &ast.FnDecl) ?string { | ||||||
| 		name = g.generic_fn_name(g.cur_concrete_types, name, true) | 		name = g.generic_fn_name(g.cur_concrete_types, name, true) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if g.pref.translated && node.attrs.contains('c') { | 	if (g.pref.translated || g.file.is_translated) && node.attrs.contains('c') { | ||||||
| 		// This fixes unknown symbols errors when building separate .c => .v files
 | 		// This fixes unknown symbols errors when building separate .c => .v files
 | ||||||
| 		// into .o files
 | 		// into .o files
 | ||||||
| 		//
 | 		//
 | ||||||
|  | @ -1176,7 +1176,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { | ||||||
| 	} else { | 	} else { | ||||||
| 		name = c_name(name) | 		name = c_name(name) | ||||||
| 	} | 	} | ||||||
| 	if g.pref.translated { | 	if g.pref.translated || g.file.is_translated { | ||||||
| 		// For `[c: 'P_TryMove'] fn p_trymove( ... `
 | 		// For `[c: 'P_TryMove'] fn p_trymove( ... `
 | ||||||
| 		// every time `p_trymove` is called, `P_TryMove` must be generated instead.
 | 		// every time `p_trymove` is called, `P_TryMove` must be generated instead.
 | ||||||
| 		if f := g.table.find_fn(node.name) { | 		if f := g.table.find_fn(node.name) { | ||||||
|  | @ -1573,7 +1573,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) { | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		elem_type := g.typ(arr_info.elem_type) | 		elem_type := g.typ(arr_info.elem_type) | ||||||
| 		if g.pref.translated && args.len == 1 { | 		if (g.pref.translated || g.file.is_translated) && args.len == 1 { | ||||||
| 			// Handle `foo(c'str')` for `fn foo(args ...&u8)`
 | 			// Handle `foo(c'str')` for `fn foo(args ...&u8)`
 | ||||||
| 			// TODOC2V handle this in a better place
 | 			// TODOC2V handle this in a better place
 | ||||||
| 			// println(g.table.type_to_str(args[0].typ))
 | 			// println(g.table.type_to_str(args[0].typ))
 | ||||||
|  |  | ||||||
|  | @ -158,8 +158,9 @@ fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comme | ||||||
| 						iv := lx.info as ast.IdentVar | 						iv := lx.info as ast.IdentVar | ||||||
| 						share = iv.share | 						share = iv.share | ||||||
| 						if iv.is_static { | 						if iv.is_static { | ||||||
| 							if !p.pref.translated && !p.pref.is_fmt && !p.inside_unsafe_fn { | 							if !p.pref.translated && !p.is_translated && !p.pref.is_fmt | ||||||
| 								return p.error_with_pos('static variables are supported only in -translated mode or in [unsafe] fn', | 								&& !p.inside_unsafe_fn { | ||||||
|  | 								return p.error_with_pos('static variables are supported only in translated mode or in [unsafe] fn', | ||||||
| 									lx.pos) | 									lx.pos) | ||||||
| 							} | 							} | ||||||
| 							is_static = true | 							is_static = true | ||||||
|  |  | ||||||
|  | @ -255,7 +255,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl { | ||||||
| 	if p.tok.kind == .name { | 	if p.tok.kind == .name { | ||||||
| 		// TODO high order fn
 | 		// TODO high order fn
 | ||||||
| 		name = if language == .js { p.check_js_name() } else { p.check_name() } | 		name = if language == .js { p.check_js_name() } else { p.check_name() } | ||||||
| 		if language == .v && !p.pref.translated && util.contains_capital(name) && !p.builtin_mod { | 		if language == .v && !p.pref.translated && !p.is_translated && util.contains_capital(name) | ||||||
|  | 			&& !p.builtin_mod { | ||||||
| 			p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead', | 			p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead', | ||||||
| 				name_pos) | 				name_pos) | ||||||
| 			return ast.FnDecl{ | 			return ast.FnDecl{ | ||||||
|  |  | ||||||
|  | @ -63,6 +63,7 @@ mut: | ||||||
| 	is_manualfree             bool       // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree
 | 	is_manualfree             bool       // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree
 | ||||||
| 	has_globals               bool       // `[has_globals] module abc` - allow globals declarations, even without -enable-globals, in that single .v file __only__
 | 	has_globals               bool       // `[has_globals] module abc` - allow globals declarations, even without -enable-globals, in that single .v file __only__
 | ||||||
| 	is_generated              bool       // `[generated] module abc` - turn off compiler notices for that single .v file __only__.
 | 	is_generated              bool       // `[generated] module abc` - turn off compiler notices for that single .v file __only__.
 | ||||||
|  | 	is_translated             bool       // `[translated] module abc` - mark a file as translated, to relax some compiler checks for translated code.
 | ||||||
| 	attrs                     []ast.Attr // attributes before next decl stmt
 | 	attrs                     []ast.Attr // attributes before next decl stmt
 | ||||||
| 	expr_mod                  string     // for constructing full type names in parse_type()
 | 	expr_mod                  string     // for constructing full type names in parse_type()
 | ||||||
| 	scope                     &ast.Scope | 	scope                     &ast.Scope | ||||||
|  | @ -324,6 +325,7 @@ pub fn (mut p Parser) parse() &ast.File { | ||||||
| 		path_base: p.file_base | 		path_base: p.file_base | ||||||
| 		is_test: p.inside_test_file | 		is_test: p.inside_test_file | ||||||
| 		is_generated: p.is_generated | 		is_generated: p.is_generated | ||||||
|  | 		is_translated: p.is_translated | ||||||
| 		nr_lines: p.scanner.line_nr | 		nr_lines: p.scanner.line_nr | ||||||
| 		nr_bytes: p.scanner.text.len | 		nr_bytes: p.scanner.text.len | ||||||
| 		mod: module_decl | 		mod: module_decl | ||||||
|  | @ -1886,7 +1888,7 @@ fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt { | ||||||
| 	// TODO remove translated
 | 	// TODO remove translated
 | ||||||
| 	if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() { | 	if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() { | ||||||
| 		return p.partial_assign_stmt(left, left_comments) | 		return p.partial_assign_stmt(left, left_comments) | ||||||
| 	} else if !p.pref.translated && !p.pref.is_fmt | 	} else if !p.pref.translated && !p.is_translated && !p.pref.is_fmt | ||||||
| 		&& tok.kind !in [.key_if, .key_match, .key_lock, .key_rlock, .key_select] { | 		&& tok.kind !in [.key_if, .key_match, .key_lock, .key_rlock, .key_select] { | ||||||
| 		for node in left { | 		for node in left { | ||||||
| 			if (is_top_level || p.tok.kind != .rcbr) && node !is ast.CallExpr | 			if (is_top_level || p.tok.kind != .rcbr) && node !is ast.CallExpr | ||||||
|  | @ -3046,6 +3048,9 @@ fn (mut p Parser) module_decl() ast.Module { | ||||||
| 							ma.pos) | 							ma.pos) | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  | 				'translated' { | ||||||
|  | 					p.is_translated = true | ||||||
|  | 				} | ||||||
| 				else { | 				else { | ||||||
| 					p.error_with_pos('unknown module attribute `[$ma.name]`', ma.pos) | 					p.error_with_pos('unknown module attribute `[$ma.name]`', ma.pos) | ||||||
| 					return mod_node | 					return mod_node | ||||||
|  | @ -3315,7 +3320,7 @@ fn (mut p Parser) global_decl() ast.GlobalDecl { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !p.has_globals && !p.pref.enable_globals && !p.pref.is_fmt && !p.pref.translated | 	if !p.has_globals && !p.pref.enable_globals && !p.pref.is_fmt && !p.pref.translated | ||||||
| 		&& !p.pref.is_livemain && !p.pref.building_v && !p.builtin_mod { | 		&& !p.is_translated && !p.pref.is_livemain && !p.pref.building_v && !p.builtin_mod { | ||||||
| 		p.error('use `v -enable-globals ...` to enable globals') | 		p.error('use `v -enable-globals ...` to enable globals') | ||||||
| 		return ast.GlobalDecl{} | 		return ast.GlobalDecl{} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -53,8 +53,8 @@ fn (mut p Parser) struct_decl() ast.StructDecl { | ||||||
| 		p.error('`$p.tok.lit` lacks body') | 		p.error('`$p.tok.lit` lacks body') | ||||||
| 		return ast.StructDecl{} | 		return ast.StructDecl{} | ||||||
| 	} | 	} | ||||||
| 	if language == .v && !p.builtin_mod && name.len > 0 && !name[0].is_capital() | 	if language == .v && !p.builtin_mod && !p.is_translated && name.len > 0 && !name[0].is_capital() | ||||||
| 		&& !p.pref.translated { | 		&& !p.pref.translated && !p.is_translated { | ||||||
| 		p.error_with_pos('struct name `$name` must begin with capital letter', name_pos) | 		p.error_with_pos('struct name `$name` must begin with capital letter', name_pos) | ||||||
| 		return ast.StructDecl{} | 		return ast.StructDecl{} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -0,0 +1,6 @@ | ||||||
|  | [translated] | ||||||
|  | module main | ||||||
|  | 
 | ||||||
|  | import math | ||||||
|  | 
 | ||||||
|  | fn test_NotSnakeCaseFunction() {} | ||||||
		Loading…
	
		Reference in New Issue