parent
							
								
									9d0a5942ac
								
							
						
					
					
						commit
						7178367de0
					
				|  | @ -534,6 +534,7 @@ pub mut: | ||||||
| 	should_be_skipped  bool   // true for calls to `[if someflag?]` functions, when there is no `-d someflag`
 | 	should_be_skipped  bool   // true for calls to `[if someflag?]` functions, when there is no `-d someflag`
 | ||||||
| 	concrete_types     []Type // concrete types, e.g. <int, string>
 | 	concrete_types     []Type // concrete types, e.g. <int, string>
 | ||||||
| 	concrete_list_pos  token.Pos | 	concrete_list_pos  token.Pos | ||||||
|  | 	raw_concrete_types []Type | ||||||
| 	free_receiver      bool // true if the receiver expression needs to be freed
 | 	free_receiver      bool // true if the receiver expression needs to be freed
 | ||||||
| 	scope              &Scope | 	scope              &Scope | ||||||
| 	from_embed_types   []Type // holds the type of the embed that the method is called from
 | 	from_embed_types   []Type // holds the type of the embed that the method is called from
 | ||||||
|  |  | ||||||
|  | @ -135,6 +135,8 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 		} else if right is ast.ComptimeSelector { | ||||||
|  | 			right_type = c.comptime_fields_default_type | ||||||
| 		} | 		} | ||||||
| 		if is_decl { | 		if is_decl { | ||||||
| 			// check generic struct init and return unwrap generic struct type
 | 			// check generic struct init and return unwrap generic struct type
 | ||||||
|  |  | ||||||
|  | @ -70,21 +70,22 @@ pub mut: | ||||||
| 	rlocked_names    []string // vars that are currently read-locked
 | 	rlocked_names    []string // vars that are currently read-locked
 | ||||||
| 	in_for_count     int      // if checker is currently in a for loop
 | 	in_for_count     int      // if checker is currently in a for loop
 | ||||||
| 	// checked_ident  string // to avoid infinite checker loops
 | 	// checked_ident  string // to avoid infinite checker loops
 | ||||||
| 	should_abort   bool // when too many errors/warnings/notices are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time.
 | 	should_abort              bool // when too many errors/warnings/notices are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time.
 | ||||||
| 	returns        bool | 	returns                   bool | ||||||
| 	scope_returns  bool | 	scope_returns             bool | ||||||
| 	is_builtin_mod bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this
 | 	is_builtin_mod            bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this
 | ||||||
| 	is_generated   bool // true for `[generated] module xyz` .v files
 | 	is_generated              bool // true for `[generated] module xyz` .v files
 | ||||||
| 	inside_unsafe  bool // true inside `unsafe {}` blocks
 | 	inside_unsafe             bool // true inside `unsafe {}` blocks
 | ||||||
| 	inside_const   bool // true inside `const ( ... )` blocks
 | 	inside_const              bool // true inside `const ( ... )` blocks
 | ||||||
| 	inside_anon_fn bool // true inside `fn() { ... }()`
 | 	inside_anon_fn            bool // true inside `fn() { ... }()`
 | ||||||
| 	inside_ref_lit bool // true inside `a := &something`
 | 	inside_ref_lit            bool // true inside `a := &something`
 | ||||||
| 	inside_defer   bool // true inside `defer {}` blocks
 | 	inside_defer              bool // true inside `defer {}` blocks
 | ||||||
| 	inside_fn_arg  bool // `a`, `b` in `a.f(b)`
 | 	inside_fn_arg             bool // `a`, `b` in `a.f(b)`
 | ||||||
| 	inside_ct_attr bool // true inside `[if expr]`
 | 	inside_ct_attr            bool // true inside `[if expr]`
 | ||||||
| 	skip_flags     bool // should `#flag` and `#include` be skipped
 | 	inside_comptime_for_field bool | ||||||
| 	fn_level       int  // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
 | 	skip_flags                bool // should `#flag` and `#include` be skipped
 | ||||||
| 	ct_cond_stack  []ast.Expr | 	fn_level                  int  // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
 | ||||||
|  | 	ct_cond_stack             []ast.Expr | ||||||
| mut: | mut: | ||||||
| 	stmt_level int // the nesting level inside each stmts list;
 | 	stmt_level int // the nesting level inside each stmts list;
 | ||||||
| 	// .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1`
 | 	// .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1`
 | ||||||
|  |  | ||||||
|  | @ -109,10 +109,19 @@ fn (mut c Checker) comptime_for(node ast.ComptimeFor) { | ||||||
| 		c.error('unknown type `$sym.name`', node.typ_pos) | 		c.error('unknown type `$sym.name`', node.typ_pos) | ||||||
| 	} | 	} | ||||||
| 	if node.kind == .fields { | 	if node.kind == .fields { | ||||||
| 		c.comptime_fields_type[node.val_var] = node.typ | 		if sym.kind == .struct_ { | ||||||
| 		c.comptime_fields_default_type = node.typ | 			sym_info := sym.info as ast.Struct | ||||||
|  | 			c.inside_comptime_for_field = true | ||||||
|  | 			for field in sym_info.fields { | ||||||
|  | 				c.comptime_fields_type[node.val_var] = node.typ | ||||||
|  | 				c.comptime_fields_default_type = field.typ | ||||||
|  | 				c.stmts(node.stmts) | ||||||
|  | 			} | ||||||
|  | 			c.inside_comptime_for_field = false | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		c.stmts(node.stmts) | ||||||
| 	} | 	} | ||||||
| 	c.stmts(node.stmts) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // comptime const eval
 | // comptime const eval
 | ||||||
|  |  | ||||||
|  | @ -428,6 +428,7 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) | ||||||
| 	} | 	} | ||||||
| 	mut has_generic := false // foo<T>() instead of foo<int>()
 | 	mut has_generic := false // foo<T>() instead of foo<int>()
 | ||||||
| 	mut concrete_types := []ast.Type{} | 	mut concrete_types := []ast.Type{} | ||||||
|  | 	node.concrete_types = node.raw_concrete_types | ||||||
| 	for concrete_type in node.concrete_types { | 	for concrete_type in node.concrete_types { | ||||||
| 		if concrete_type.has_flag(.generic) { | 		if concrete_type.has_flag(.generic) { | ||||||
| 			has_generic = true | 			has_generic = true | ||||||
|  | @ -777,6 +778,13 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) | ||||||
| 
 | 
 | ||||||
| 		typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr)) | 		typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr)) | ||||||
| 		node.args[i].typ = typ | 		node.args[i].typ = typ | ||||||
|  | 		if c.inside_comptime_for_field { | ||||||
|  | 			if mut call_arg.expr is ast.Ident { | ||||||
|  | 				if mut call_arg.expr.obj is ast.Var { | ||||||
|  | 					node.args[i].typ = call_arg.expr.obj.typ | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 		typ_sym := c.table.sym(typ) | 		typ_sym := c.table.sym(typ) | ||||||
| 		param_typ_sym := c.table.sym(param.typ) | 		param_typ_sym := c.table.sym(param.typ) | ||||||
| 		if func.is_variadic && typ.has_flag(.variadic) && node.args.len - 1 > i { | 		if func.is_variadic && typ.has_flag(.variadic) && node.args.len - 1 > i { | ||||||
|  | @ -936,6 +944,9 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) | ||||||
| 						continue | 						continue | ||||||
| 					} | 					} | ||||||
| 					c.check_expected_call_arg(utyp, unwrap_typ, node.language, call_arg) or { | 					c.check_expected_call_arg(utyp, unwrap_typ, node.language, call_arg) or { | ||||||
|  | 						if c.comptime_fields_type.len > 0 { | ||||||
|  | 							continue | ||||||
|  | 						} | ||||||
| 						c.error('$err.msg() in argument ${i + 1} to `$fn_name`', call_arg.pos) | 						c.error('$err.msg() in argument ${i + 1} to `$fn_name`', call_arg.pos) | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -18,20 +18,6 @@ vlib/v/checker/tests/comptime_for.vv:4:12: error: unknown type `T` | ||||||
|       |               ^ |       |               ^ | ||||||
|     5 |         $if f.typ is Huh {} |     5 |         $if f.typ is Huh {} | ||||||
|     6 |         $if f.typ is T {} |     6 |         $if f.typ is T {} | ||||||
| vlib/v/checker/tests/comptime_for.vv:5:16: error: unknown type `Huh` |  | ||||||
|     3 |     $for f in Huh.fields {} |  | ||||||
|     4 |     $for f in T.fields { |  | ||||||
|     5 |         $if f.typ is Huh {} |  | ||||||
|       |                      ~~~ |  | ||||||
|     6 |         $if f.typ is T {} |  | ||||||
|     7 |     } |  | ||||||
| vlib/v/checker/tests/comptime_for.vv:6:16: error: unknown type `T` |  | ||||||
|     4 |     $for f in T.fields { |  | ||||||
|     5 |         $if f.typ is Huh {} |  | ||||||
|     6 |         $if f.typ is T {} |  | ||||||
|       |                      ^ |  | ||||||
|     7 |     } |  | ||||||
|     8 |     _ = m |  | ||||||
| vlib/v/checker/tests/comptime_for.vv:8:6: error: undefined ident: `m` | vlib/v/checker/tests/comptime_for.vv:8:6: error: undefined ident: `m` | ||||||
|     6 |         $if f.typ is T {} |     6 |         $if f.typ is T {} | ||||||
|     7 |     } |     7 |     } | ||||||
|  |  | ||||||
|  | @ -20,4 +20,6 @@ struct S1 { | ||||||
| 	i int | 	i int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| gf<S1>() | fn main() { | ||||||
|  | 	gf<S1>() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -46,122 +46,123 @@ struct Gen { | ||||||
| 	timers_should_print bool | 	timers_should_print bool | ||||||
| 	table               &ast.Table | 	table               &ast.Table | ||||||
| mut: | mut: | ||||||
| 	out                    strings.Builder | 	out                       strings.Builder | ||||||
| 	cheaders               strings.Builder | 	cheaders                  strings.Builder | ||||||
| 	includes               strings.Builder // all C #includes required by V modules
 | 	includes                  strings.Builder // all C #includes required by V modules
 | ||||||
| 	typedefs               strings.Builder | 	typedefs                  strings.Builder | ||||||
| 	enum_typedefs          strings.Builder // enum types
 | 	enum_typedefs             strings.Builder // enum types
 | ||||||
| 	definitions            strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
 | 	definitions               strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
 | ||||||
| 	type_definitions       strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
 | 	type_definitions          strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
 | ||||||
| 	alias_definitions      strings.Builder // alias fixed array of non-builtin
 | 	alias_definitions         strings.Builder // alias fixed array of non-builtin
 | ||||||
| 	hotcode_definitions    strings.Builder // -live declarations & functions
 | 	hotcode_definitions       strings.Builder // -live declarations & functions
 | ||||||
| 	channel_definitions    strings.Builder // channel related code
 | 	channel_definitions       strings.Builder // channel related code
 | ||||||
| 	comptime_definitions   strings.Builder // custom defines, given by -d/-define flags on the CLI
 | 	comptime_definitions      strings.Builder // custom defines, given by -d/-define flags on the CLI
 | ||||||
| 	global_inits           map[string]strings.Builder // default initializers for globals (goes in _vinit())
 | 	global_inits              map[string]strings.Builder // default initializers for globals (goes in _vinit())
 | ||||||
| 	global_init            strings.Builder // thread local of the above
 | 	global_init               strings.Builder // thread local of the above
 | ||||||
| 	inits                  map[string]strings.Builder // contents of `void _vinit/2{}`
 | 	inits                     map[string]strings.Builder // contents of `void _vinit/2{}`
 | ||||||
| 	init                   strings.Builder | 	init                      strings.Builder | ||||||
| 	cleanup                strings.Builder | 	cleanup                   strings.Builder | ||||||
| 	cleanups               map[string]strings.Builder // contents of `void _vcleanup(){}`
 | 	cleanups                  map[string]strings.Builder // contents of `void _vcleanup(){}`
 | ||||||
| 	gowrappers             strings.Builder // all go callsite wrappers
 | 	gowrappers                strings.Builder // all go callsite wrappers
 | ||||||
| 	stringliterals         strings.Builder // all string literals (they depend on tos3() beeing defined
 | 	stringliterals            strings.Builder // all string literals (they depend on tos3() beeing defined
 | ||||||
| 	auto_str_funcs         strings.Builder // function bodies of all auto generated _str funcs
 | 	auto_str_funcs            strings.Builder // function bodies of all auto generated _str funcs
 | ||||||
| 	dump_funcs             strings.Builder // function bodies of all auto generated _str funcs
 | 	dump_funcs                strings.Builder // function bodies of all auto generated _str funcs
 | ||||||
| 	pcs_declarations       strings.Builder // -prof profile counter declarations for each function
 | 	pcs_declarations          strings.Builder // -prof profile counter declarations for each function
 | ||||||
| 	embedded_data          strings.Builder // data to embed in the executable/binary
 | 	embedded_data             strings.Builder // data to embed in the executable/binary
 | ||||||
| 	shared_types           strings.Builder // shared/lock types
 | 	shared_types              strings.Builder // shared/lock types
 | ||||||
| 	shared_functions       strings.Builder // shared constructors
 | 	shared_functions          strings.Builder // shared constructors
 | ||||||
| 	options                strings.Builder // `Option_xxxx` types
 | 	options                   strings.Builder // `Option_xxxx` types
 | ||||||
| 	json_forward_decls     strings.Builder // json type forward decls
 | 	json_forward_decls        strings.Builder // json type forward decls
 | ||||||
| 	sql_buf                strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
 | 	sql_buf                   strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
 | ||||||
| 	file                   &ast.File | 	file                      &ast.File | ||||||
| 	unique_file_path_hash  u64 // a hash of file.path, used for making auxilary fn generation unique (like `compare_xyz`)
 | 	unique_file_path_hash     u64 // a hash of file.path, used for making auxilary fn generation unique (like `compare_xyz`)
 | ||||||
| 	fn_decl                &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
 | 	fn_decl                   &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
 | ||||||
| 	last_fn_c_name         string | 	last_fn_c_name            string | ||||||
| 	tmp_count              int  // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn.
 | 	tmp_count                 int  // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn.
 | ||||||
| 	tmp_count_af           int  // a separate tmp var counter for autofree fn calls
 | 	tmp_count_af              int  // a separate tmp var counter for autofree fn calls
 | ||||||
| 	tmp_count_declarations int  // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations
 | 	tmp_count_declarations    int  // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations
 | ||||||
| 	global_tmp_count       int  // like tmp_count but global and not resetted in each function
 | 	global_tmp_count          int  // like tmp_count but global and not resetted in each function
 | ||||||
| 	discard_or_result      bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage
 | 	discard_or_result         bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage
 | ||||||
| 	is_assign_lhs          bool // inside left part of assign expr (for array_set(), etc)
 | 	is_assign_lhs             bool // inside left part of assign expr (for array_set(), etc)
 | ||||||
| 	is_void_expr_stmt      bool // ExprStmt whos result is discarded
 | 	is_void_expr_stmt         bool // ExprStmt whos result is discarded
 | ||||||
| 	is_arraymap_set        bool // map or array set value state
 | 	is_arraymap_set           bool // map or array set value state
 | ||||||
| 	is_amp                 bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
 | 	is_amp                    bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
 | ||||||
| 	is_sql                 bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
 | 	is_sql                    bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
 | ||||||
| 	is_shared              bool // for initialization of hidden mutex in `[rw]shared` literals
 | 	is_shared                 bool // for initialization of hidden mutex in `[rw]shared` literals
 | ||||||
| 	is_vlines_enabled      bool // is it safe to generate #line directives when -g is passed
 | 	is_vlines_enabled         bool // is it safe to generate #line directives when -g is passed
 | ||||||
| 	is_autofree            bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree
 | 	is_autofree               bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree
 | ||||||
| 	is_builtin_mod         bool | 	is_builtin_mod            bool | ||||||
| 	is_json_fn             bool // inside json.encode()
 | 	is_json_fn                bool // inside json.encode()
 | ||||||
| 	is_js_call             bool // for handling a special type arg #1 `json.decode(User, ...)`
 | 	is_js_call                bool // for handling a special type arg #1 `json.decode(User, ...)`
 | ||||||
| 	is_fn_index_call       bool | 	is_fn_index_call          bool | ||||||
| 	vlines_path            string // set to the proper path for generating #line directives
 | 	vlines_path               string // set to the proper path for generating #line directives
 | ||||||
| 	optionals              map[string]string // to avoid duplicates
 | 	optionals                 map[string]string // to avoid duplicates
 | ||||||
| 	done_optionals         shared []string   // to avoid duplicates
 | 	done_optionals            shared []string   // to avoid duplicates
 | ||||||
| 	chan_pop_optionals     map[string]string // types for `x := <-ch or {...}`
 | 	chan_pop_optionals        map[string]string // types for `x := <-ch or {...}`
 | ||||||
| 	chan_push_optionals    map[string]string // types for `ch <- x or {...}`
 | 	chan_push_optionals       map[string]string // types for `ch <- x or {...}`
 | ||||||
| 	mtxs                   string // array of mutexes if the `lock` has multiple variables
 | 	mtxs                      string // array of mutexes if the `lock` has multiple variables
 | ||||||
| 	labeled_loops          map[string]&ast.Stmt | 	labeled_loops             map[string]&ast.Stmt | ||||||
| 	inner_loop             &ast.Stmt | 	inner_loop                &ast.Stmt | ||||||
| 	shareds                map[int]string // types with hidden mutex for which decl has been emitted
 | 	shareds                   map[int]string // types with hidden mutex for which decl has been emitted
 | ||||||
| 	inside_ternary         int  // ?: comma separated statements on a single line
 | 	inside_ternary            int  // ?: comma separated statements on a single line
 | ||||||
| 	inside_map_postfix     bool // inside map++/-- postfix expr
 | 	inside_map_postfix        bool // inside map++/-- postfix expr
 | ||||||
| 	inside_map_infix       bool // inside map<</+=/-= infix expr
 | 	inside_map_infix          bool // inside map<</+=/-= infix expr
 | ||||||
| 	inside_map_index       bool | 	inside_map_index          bool | ||||||
| 	inside_opt_data        bool | 	inside_opt_data           bool | ||||||
| 	inside_if_optional     bool | 	inside_if_optional        bool | ||||||
| 	inside_match_optional  bool | 	inside_match_optional     bool | ||||||
| 	inside_vweb_tmpl       bool | 	inside_vweb_tmpl          bool | ||||||
| 	inside_return          bool | 	inside_return             bool | ||||||
| 	inside_struct_init     bool | 	inside_struct_init        bool | ||||||
| 	inside_or_block        bool | 	inside_or_block           bool | ||||||
| 	inside_call            bool | 	inside_call               bool | ||||||
| 	inside_for_c_stmt      bool | 	inside_for_c_stmt         bool | ||||||
| 	inside_cast_in_heap    int // inside cast to interface type in heap (resolve recursive calls)
 | 	inside_comptime_for_field bool | ||||||
| 	inside_const           bool | 	inside_cast_in_heap       int // inside cast to interface type in heap (resolve recursive calls)
 | ||||||
| 	inside_lambda          bool | 	inside_const              bool | ||||||
| 	loop_depth             int | 	inside_lambda             bool | ||||||
| 	ternary_names          map[string]string | 	loop_depth                int | ||||||
| 	ternary_level_names    map[string][]string | 	ternary_names             map[string]string | ||||||
| 	arraymap_set_pos       int   // map or array set value position
 | 	ternary_level_names       map[string][]string | ||||||
| 	stmt_path_pos          []int // positions of each statement start, for inserting C statements before the current statement
 | 	arraymap_set_pos          int   // map or array set value position
 | ||||||
| 	skip_stmt_pos          bool  // for handling if expressions + autofree (since both prepend C statements)
 | 	stmt_path_pos             []int // positions of each statement start, for inserting C statements before the current statement
 | ||||||
| 	right_is_opt           bool | 	skip_stmt_pos             bool  // for handling if expressions + autofree (since both prepend C statements)
 | ||||||
| 	indent                 int | 	right_is_opt              bool | ||||||
| 	empty_line             bool | 	indent                    int | ||||||
| 	assign_op              token.Kind // *=, =, etc (for array_set)
 | 	empty_line                bool | ||||||
| 	defer_stmts            []ast.DeferStmt | 	assign_op                 token.Kind // *=, =, etc (for array_set)
 | ||||||
| 	defer_ifdef            string | 	defer_stmts               []ast.DeferStmt | ||||||
| 	defer_profile_code     string | 	defer_ifdef               string | ||||||
| 	defer_vars             []string | 	defer_profile_code        string | ||||||
| 	str_types              []StrType       // types that need automatic str() generation
 | 	defer_vars                []string | ||||||
| 	generated_str_fns      []StrType       // types that already have a str() function
 | 	str_types                 []StrType       // types that need automatic str() generation
 | ||||||
| 	threaded_fns           shared []string // for generating unique wrapper types and fns for `go xxx()`
 | 	generated_str_fns         []StrType       // types that already have a str() function
 | ||||||
| 	waiter_fns             shared []string // functions that wait for `go xxx()` to finish
 | 	threaded_fns              shared []string // for generating unique wrapper types and fns for `go xxx()`
 | ||||||
| 	needed_equality_fns    []ast.Type | 	waiter_fns                shared []string // functions that wait for `go xxx()` to finish
 | ||||||
| 	generated_eq_fns       []ast.Type | 	needed_equality_fns       []ast.Type | ||||||
| 	array_sort_fn          shared []string | 	generated_eq_fns          []ast.Type | ||||||
| 	array_contains_types   []ast.Type | 	array_sort_fn             shared []string | ||||||
| 	array_index_types      []ast.Type | 	array_contains_types      []ast.Type | ||||||
| 	auto_fn_definitions    []string // auto generated functions defination list
 | 	array_index_types         []ast.Type | ||||||
| 	sumtype_casting_fns    []SumtypeCastingFn | 	auto_fn_definitions       []string // auto generated functions defination list
 | ||||||
| 	anon_fn_definitions    []string     // anon generated functions defination list
 | 	sumtype_casting_fns       []SumtypeCastingFn | ||||||
| 	sumtype_definitions    map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated
 | 	anon_fn_definitions       []string     // anon generated functions defination list
 | ||||||
| 	json_types             []ast.Type   // to avoid json gen duplicates
 | 	sumtype_definitions       map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated
 | ||||||
| 	pcs                    []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
 | 	json_types                []ast.Type   // to avoid json gen duplicates
 | ||||||
| 	hotcode_fn_names       []string | 	pcs                       []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
 | ||||||
| 	embedded_files         []ast.EmbeddedFile | 	hotcode_fn_names          []string | ||||||
| 	sql_i                  int | 	embedded_files            []ast.EmbeddedFile | ||||||
| 	sql_stmt_name          string | 	sql_i                     int | ||||||
| 	sql_bind_name          string | 	sql_stmt_name             string | ||||||
| 	sql_idents             []string | 	sql_bind_name             string | ||||||
| 	sql_idents_types       []ast.Type | 	sql_idents                []string | ||||||
| 	sql_left_type          ast.Type | 	sql_idents_types          []ast.Type | ||||||
| 	sql_table_name         string | 	sql_left_type             ast.Type | ||||||
| 	sql_fkey               string | 	sql_table_name            string | ||||||
| 	sql_parent_id          string | 	sql_fkey                  string | ||||||
| 	sql_side               SqlExprSide // left or right, to distinguish idents in `name == name`
 | 	sql_parent_id             string | ||||||
| 	strs_to_free0          []string    // strings.Builder
 | 	sql_side                  SqlExprSide // left or right, to distinguish idents in `name == name`
 | ||||||
|  | 	strs_to_free0             []string    // strings.Builder
 | ||||||
| 	// strs_to_free          []string // strings.Builder
 | 	// strs_to_free          []string // strings.Builder
 | ||||||
| 	// tmp_arg_vars_to_free  []string
 | 	// tmp_arg_vars_to_free  []string
 | ||||||
| 	// autofree_pregen       map[string]string
 | 	// autofree_pregen       map[string]string
 | ||||||
|  |  | ||||||
|  | @ -503,13 +503,16 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { | ||||||
| 		} | 		} | ||||||
| 	} else if node.kind == .fields { | 	} else if node.kind == .fields { | ||||||
| 		// TODO add fields
 | 		// TODO add fields
 | ||||||
| 		if sym.info is ast.Struct { | 		if sym.kind == .struct_ { | ||||||
| 			if sym.info.fields.len > 0 { | 			sym_info := sym.info as ast.Struct | ||||||
|  | 			if sym_info.fields.len > 0 { | ||||||
| 				g.writeln('\tFieldData $node.val_var = {0};') | 				g.writeln('\tFieldData $node.val_var = {0};') | ||||||
| 			} | 			} | ||||||
| 			for field in sym.info.fields { | 			g.inside_comptime_for_field = true | ||||||
|  | 			for field in sym_info.fields { | ||||||
| 				g.comptime_for_field_var = node.val_var | 				g.comptime_for_field_var = node.val_var | ||||||
| 				g.comptime_for_field_value = field | 				g.comptime_for_field_value = field | ||||||
|  | 				g.comptime_for_field_type = field.typ | ||||||
| 				g.writeln('/* field $i */ {') | 				g.writeln('/* field $i */ {') | ||||||
| 				g.writeln('\t${node.val_var}.name = _SLIT("$field.name");') | 				g.writeln('\t${node.val_var}.name = _SLIT("$field.name");') | ||||||
| 				if field.attrs.len == 0 { | 				if field.attrs.len == 0 { | ||||||
|  | @ -531,7 +534,9 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) { | ||||||
| 				g.stmts(node.stmts) | 				g.stmts(node.stmts) | ||||||
| 				i++ | 				i++ | ||||||
| 				g.writeln('}') | 				g.writeln('}') | ||||||
|  | 				g.comptime_for_field_type = 0 | ||||||
| 			} | 			} | ||||||
|  | 			g.inside_comptime_for_field = false | ||||||
| 			g.comptime_var_type_map.delete(node.val_var) | 			g.comptime_var_type_map.delete(node.val_var) | ||||||
| 		} | 		} | ||||||
| 	} else if node.kind == .attributes { | 	} else if node.kind == .attributes { | ||||||
|  |  | ||||||
|  | @ -1120,6 +1120,16 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { | ||||||
| 		} | 		} | ||||||
| 		is_selector_call = true | 		is_selector_call = true | ||||||
| 	} | 	} | ||||||
|  | 	if g.inside_comptime_for_field { | ||||||
|  | 		mut node_ := unsafe { node } | ||||||
|  | 		for i, mut call_arg in node_.args { | ||||||
|  | 			if mut call_arg.expr is ast.Ident { | ||||||
|  | 				if mut call_arg.expr.obj is ast.Var { | ||||||
|  | 					node_.args[i].typ = call_arg.expr.obj.typ | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	mut name := node.name | 	mut name := node.name | ||||||
| 	is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic'] | 	is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic'] | ||||||
| 	print_method := name | 	print_method := name | ||||||
|  | @ -1201,14 +1211,22 @@ fn (mut g Gen) fn_call(node ast.CallExpr) { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if !is_selector_call { | 	if !is_selector_call { | ||||||
| 		name = g.generic_fn_name(node.concrete_types, name, false) | 		if func := g.table.find_fn(node.name) { | ||||||
|  | 			if func.generic_names.len > 0 { | ||||||
|  | 				if g.comptime_for_field_type != 0 && g.inside_comptime_for_field { | ||||||
|  | 					name = g.generic_fn_name([g.comptime_for_field_type], name, false) | ||||||
|  | 				} else { | ||||||
|  | 					name = g.generic_fn_name(node.concrete_types, name, false) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	// TODO2
 | 	// TODO2
 | ||||||
| 	// cgen shouldn't modify ast nodes, this should be moved
 | 	// cgen shouldn't modify ast nodes, this should be moved
 | ||||||
| 	// g.generate_tmp_autofree_arg_vars(node, name)
 | 	// g.generate_tmp_autofree_arg_vars(node, name)
 | ||||||
| 	// Handle `print(x)`
 | 	// Handle `print(x)`
 | ||||||
| 	mut print_auto_str := false | 	mut print_auto_str := false | ||||||
| 	if is_print && (node.args[0].typ != ast.string_type || g.comptime_for_method.len > 0) { // && !free_tmp_arg_vars {
 | 	if is_print && (node.args[0].typ != ast.string_type || g.comptime_for_method.len > 0) { | ||||||
| 		mut typ := node.args[0].typ | 		mut typ := node.args[0].typ | ||||||
| 		if typ == 0 { | 		if typ == 0 { | ||||||
| 			g.checker_bug('print arg.typ is 0', node.pos) | 			g.checker_bug('print arg.typ is 0', node.pos) | ||||||
|  |  | ||||||
|  | @ -197,6 +197,16 @@ fn (mut g Gen) str_val(node ast.StringInterLiteral, i int) { | ||||||
| 
 | 
 | ||||||
| fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { | fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { | ||||||
| 	// fn (mut g Gen) str_int2(node ast.StringInterLiteral) {
 | 	// fn (mut g Gen) str_int2(node ast.StringInterLiteral) {
 | ||||||
|  | 	if g.inside_comptime_for_field { | ||||||
|  | 		mut node_ := unsafe { node } | ||||||
|  | 		for i, expr in node_.exprs { | ||||||
|  | 			if mut expr is ast.Ident { | ||||||
|  | 				if mut expr.obj is ast.Var { | ||||||
|  | 					node_.expr_types[i] = expr.obj.typ | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	g.write(' str_intp($node.vals.len, ') | 	g.write(' str_intp($node.vals.len, ') | ||||||
| 	g.write('_MOV((StrIntpData[]){') | 	g.write('_MOV((StrIntpData[]){') | ||||||
| 	for i, val in node.vals { | 	for i, val in node.vals { | ||||||
|  |  | ||||||
|  | @ -92,6 +92,7 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr | ||||||
| 		language: language | 		language: language | ||||||
| 		concrete_types: concrete_types | 		concrete_types: concrete_types | ||||||
| 		concrete_list_pos: concrete_list_pos | 		concrete_list_pos: concrete_list_pos | ||||||
|  | 		raw_concrete_types: concrete_types | ||||||
| 		or_block: ast.OrExpr{ | 		or_block: ast.OrExpr{ | ||||||
| 			stmts: or_stmts | 			stmts: or_stmts | ||||||
| 			kind: or_kind | 			kind: or_kind | ||||||
|  |  | ||||||
|  | @ -0,0 +1,49 @@ | ||||||
|  | module main | ||||||
|  | 
 | ||||||
|  | // exploring `decode` options with nested structs
 | ||||||
|  | 
 | ||||||
|  | struct Parent { | ||||||
|  | 	name  string | ||||||
|  | 	age   int | ||||||
|  | 	child Child | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct Child { | ||||||
|  | 	name string | ||||||
|  | 	age  int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn inspect<T>(t T) string { | ||||||
|  | 	mut output_str := '' | ||||||
|  | 	println('$T.name') | ||||||
|  | 	$for field in T.fields { | ||||||
|  | 		val := t.$(field.name) | ||||||
|  | 		$if field.typ is string { | ||||||
|  | 			println('  $field.name = $val') | ||||||
|  | 			output_str += '  $field.name = $val\n' | ||||||
|  | 		} $else $if field.typ is int { | ||||||
|  | 			println('  $field.name = $val') | ||||||
|  | 			output_str += '  $field.name = $val\n' | ||||||
|  | 		} $else { | ||||||
|  | 			str := inspect(val) | ||||||
|  | 			output_str += str | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return output_str | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn test_comptime_for_in_field_with_generic_fn() { | ||||||
|  | 	p := Parent{ | ||||||
|  | 		name: 'parent' | ||||||
|  | 		age: 30 | ||||||
|  | 		child: Child{ | ||||||
|  | 			name: 'child' | ||||||
|  | 			age: 5 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	ret := inspect(p) | ||||||
|  | 	assert ret.contains('name = parent') | ||||||
|  | 	assert ret.contains('age = 30') | ||||||
|  | 	assert ret.contains('name = child') | ||||||
|  | 	assert ret.contains('age = 5') | ||||||
|  | } | ||||||
|  | @ -39,7 +39,7 @@ fn decode2<T>() T { | ||||||
| 			x.$(field.name) = byte(-1) | 			x.$(field.name) = byte(-1) | ||||||
| 		} $else $if field.typ is int { | 		} $else $if field.typ is int { | ||||||
| 			x.$(field.name) = int(-1) | 			x.$(field.name) = int(-1) | ||||||
| 		} $else { | 		} $else $if field.typ is string { | ||||||
| 			x.$(field.name) = 'hi' | 			x.$(field.name) = 'hi' | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue