all: implement basic comptime field selector (#7888)
							parent
							
								
									e19277352b
								
							
						
					
					
						commit
						5841d5d8e1
					
				| 
						 | 
				
			
			@ -10,11 +10,11 @@ import v.errors
 | 
			
		|||
pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl
 | 
			
		||||
 | 
			
		||||
pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral |
 | 
			
		||||
	CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ConcatExpr |
 | 
			
		||||
	EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral |
 | 
			
		||||
	Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr |
 | 
			
		||||
	RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral | StringLiteral |
 | 
			
		||||
	StructInit | Type | TypeOf | UnsafeExpr
 | 
			
		||||
	CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | ComptimeSelector |
 | 
			
		||||
	ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr |
 | 
			
		||||
	IntegerLiteral | Likely | LockExpr | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr |
 | 
			
		||||
	PrefixExpr | RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | StringInterLiteral |
 | 
			
		||||
	StringLiteral | StructInit | Type | TypeOf | UnsafeExpr
 | 
			
		||||
 | 
			
		||||
pub type Stmt = AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | DeferStmt |
 | 
			
		||||
	EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
 | 
			
		||||
| 
						 | 
				
			
			@ -1056,8 +1056,19 @@ pub mut:
 | 
			
		|||
	val  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct ComptimeSelector {
 | 
			
		||||
pub:
 | 
			
		||||
	has_parens bool // if $() is used, for vfmt
 | 
			
		||||
	left       Expr
 | 
			
		||||
	field_expr Expr
 | 
			
		||||
pub mut:
 | 
			
		||||
	left_type  table.Type
 | 
			
		||||
	typ        table.Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct ComptimeCall {
 | 
			
		||||
pub:
 | 
			
		||||
	has_parens  bool // if $() is used, for vfmt
 | 
			
		||||
	method_name string
 | 
			
		||||
	left        Expr
 | 
			
		||||
	is_vweb     bool
 | 
			
		||||
| 
						 | 
				
			
			@ -1147,7 +1158,7 @@ pub fn (expr Expr) position() token.Position {
 | 
			
		|||
		IfGuardExpr {
 | 
			
		||||
			return expr.expr.position()
 | 
			
		||||
		}
 | 
			
		||||
		ComptimeCall {
 | 
			
		||||
		ComptimeCall, ComptimeSelector {
 | 
			
		||||
			return expr.left.position()
 | 
			
		||||
		}
 | 
			
		||||
		InfixExpr {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -212,6 +212,9 @@ pub fn (x Expr) str() string {
 | 
			
		|||
		CharLiteral {
 | 
			
		||||
			return '`$x.val`'
 | 
			
		||||
		}
 | 
			
		||||
		ComptimeSelector {
 | 
			
		||||
			return '${x.left}.$$x.field_expr'
 | 
			
		||||
		}
 | 
			
		||||
		EnumVal {
 | 
			
		||||
			return '.$x.val'
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,6 +69,7 @@ mut:
 | 
			
		|||
	prevent_sum_type_unwrapping_once bool   // needed for assign new values to sum type, stopping unwrapping then
 | 
			
		||||
	loop_label                       string // set when inside a labelled for loop
 | 
			
		||||
	timers                           &util.Timers = util.new_timers(false)
 | 
			
		||||
	comptime_fields_type             map[string]table.Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn new_checker(table &table.Table, pref &pref.Preferences) Checker {
 | 
			
		||||
| 
						 | 
				
			
			@ -979,6 +980,9 @@ fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) {
 | 
			
		|||
			// TODO
 | 
			
		||||
			return '', pos
 | 
			
		||||
		}
 | 
			
		||||
		ast.ComptimeSelector {
 | 
			
		||||
			return '', pos
 | 
			
		||||
		}
 | 
			
		||||
		ast.Ident {
 | 
			
		||||
			if expr.obj is ast.Var {
 | 
			
		||||
				mut v := expr.obj as ast.Var
 | 
			
		||||
| 
						 | 
				
			
			@ -3201,10 +3205,26 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
 | 
			
		|||
			}
 | 
			
		||||
			if node.method_name == 'html' {
 | 
			
		||||
				return c.table.find_type_idx('vweb.Result')
 | 
			
		||||
			} else {
 | 
			
		||||
			}
 | 
			
		||||
			return table.string_type
 | 
			
		||||
		}
 | 
			
		||||
			// return table.void_type
 | 
			
		||||
		ast.ComptimeSelector {
 | 
			
		||||
			node.left_type = c.unwrap_generic(c.expr(node.left))
 | 
			
		||||
			expr_type := c.unwrap_generic(c.expr(node.field_expr))
 | 
			
		||||
			expr_sym := c.table.get_type_symbol(expr_type)
 | 
			
		||||
			if expr_type != table.string_type {
 | 
			
		||||
				c.error('expected `string` instead of `$expr_sym.name` (e.g. `field.name`)',
 | 
			
		||||
					node.field_expr.position())
 | 
			
		||||
			}
 | 
			
		||||
			if node.field_expr is ast.SelectorExpr {
 | 
			
		||||
				expr_name := node.field_expr.expr.str()
 | 
			
		||||
				if expr_name in c.comptime_fields_type {
 | 
			
		||||
					return c.comptime_fields_type[expr_name]
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			c.error('compile time field access can only be used when iterating over `T.fields`',
 | 
			
		||||
				node.field_expr.position())
 | 
			
		||||
			return table.void_type
 | 
			
		||||
		}
 | 
			
		||||
		ast.ConcatExpr {
 | 
			
		||||
			return c.concat_expr(mut node)
 | 
			
		||||
| 
						 | 
				
			
			@ -4210,6 +4230,14 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if node.is_comptime { // Skip checking if needed
 | 
			
		||||
			// smartcast field type on comptime if
 | 
			
		||||
			if branch.cond is ast.InfixExpr {
 | 
			
		||||
				if branch.cond.op == .key_is {
 | 
			
		||||
					se := branch.cond.left as ast.SelectorExpr
 | 
			
		||||
					got_type := (branch.cond.right as ast.Type).typ
 | 
			
		||||
					c.comptime_fields_type[se.expr.str()] = got_type
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			cur_skip_flags := c.skip_flags
 | 
			
		||||
			if found_branch {
 | 
			
		||||
				c.skip_flags = true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
vlib/v/checker/tests/comptime_field_selector_not_in_for_err.vv:9:5: error: compile time field access can only be used when iterating over `T.fields`
 | 
			
		||||
    7 |     mut t := T{}
 | 
			
		||||
    8 |     name := 'test'
 | 
			
		||||
    9 |     t.$name = '3'
 | 
			
		||||
      |        ~~~~
 | 
			
		||||
   10 | }
 | 
			
		||||
   11 |
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
struct Foo {
 | 
			
		||||
	test int
 | 
			
		||||
	name string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test<T>() {
 | 
			
		||||
	mut t := T{}
 | 
			
		||||
	name := 'test'
 | 
			
		||||
	t.$name = '3'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
	test<Foo>()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
vlib/v/checker/tests/comptime_field_selector_not_name_err.vv:10:7: error: expected `string` instead of `FieldData` (e.g. `field.name`)
 | 
			
		||||
    8 |     $for f in T.fields {
 | 
			
		||||
    9 |         $if f.typ is string {
 | 
			
		||||
   10 |             t.$f = '3'
 | 
			
		||||
      |                ^
 | 
			
		||||
   11 |         }
 | 
			
		||||
   12 |     }
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
struct Foo {
 | 
			
		||||
	test int
 | 
			
		||||
	name string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test<T>() {
 | 
			
		||||
	mut t := T{}
 | 
			
		||||
	$for f in T.fields {
 | 
			
		||||
		$if f.typ is string {
 | 
			
		||||
			t.$f = '3'
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
	test<Foo>()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -924,8 +924,17 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
 | 
			
		|||
					f.write("\$tmpl('$node.args_var')")
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				f.write('${node.left}.\$${node.method_name}($node.args_var)')
 | 
			
		||||
				method_expr := if node.has_parens {
 | 
			
		||||
					'(${node.method_name}($node.args_var))'
 | 
			
		||||
				} else {
 | 
			
		||||
					'${node.method_name}($node.args_var)'
 | 
			
		||||
				}
 | 
			
		||||
				f.write('${node.left}.$$method_expr')
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		ast.ComptimeSelector {
 | 
			
		||||
			field_expr := if node.has_parens { '($node.field_expr)' } else { node.field_expr.str() }
 | 
			
		||||
			f.write('${node.left}.$$field_expr')
 | 
			
		||||
		}
 | 
			
		||||
		ast.ConcatExpr {
 | 
			
		||||
			for i, val in node.vals {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
struct Foo {
 | 
			
		||||
mut:
 | 
			
		||||
	test string
 | 
			
		||||
	name string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (f Foo) print() {
 | 
			
		||||
	println('test')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test<T>() {
 | 
			
		||||
	mut t := T{}
 | 
			
		||||
	t.name = '2'
 | 
			
		||||
	$for f in T.fields {
 | 
			
		||||
		$if f.typ is string {
 | 
			
		||||
			println(t.$f.name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
	test<Foo>()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
struct Foo {
 | 
			
		||||
mut:
 | 
			
		||||
	test string
 | 
			
		||||
	name string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (f Foo) print() {
 | 
			
		||||
	println('test')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test<T>() {
 | 
			
		||||
	mut t := T{}
 | 
			
		||||
	t.name = '2'
 | 
			
		||||
	$for f in T.fields {
 | 
			
		||||
		$if f.typ is string {
 | 
			
		||||
			println(t.$(f.name))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
	test<Foo>()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +109,10 @@ mut:
 | 
			
		|||
	inside_call                      bool
 | 
			
		||||
	has_main                         bool
 | 
			
		||||
	inside_const                     bool
 | 
			
		||||
	comp_for_method                  string // $for method in T {
 | 
			
		||||
	comp_for_method                  string      // $for method in T.methods {}
 | 
			
		||||
	comp_for_field_var               string      // $for field in T.fields {}; the variable name
 | 
			
		||||
	comp_for_field_value             table.Field // value of the field variable
 | 
			
		||||
	comp_for_field_type              table.Type  // type of the field variable inferred from `$if field.typ is T {}`
 | 
			
		||||
	comptime_var_type_map            map[string]table.Type
 | 
			
		||||
	// tmp_arg_vars_to_free  []string
 | 
			
		||||
	// autofree_pregen       map[string]string
 | 
			
		||||
| 
						 | 
				
			
			@ -2491,6 +2494,9 @@ fn (mut g Gen) expr(node ast.Expr) {
 | 
			
		|||
		ast.ComptimeCall {
 | 
			
		||||
			g.comptime_call(node)
 | 
			
		||||
		}
 | 
			
		||||
		ast.ComptimeSelector {
 | 
			
		||||
			g.comptime_selector(node)
 | 
			
		||||
		}
 | 
			
		||||
		ast.Comment {}
 | 
			
		||||
		ast.ConcatExpr {
 | 
			
		||||
			g.concat_expr(node)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,26 @@ import v.ast
 | 
			
		|||
import v.table
 | 
			
		||||
import v.util
 | 
			
		||||
 | 
			
		||||
fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) {
 | 
			
		||||
	g.expr(node.left)
 | 
			
		||||
	if node.left_type.is_ptr() {
 | 
			
		||||
		g.write('->')
 | 
			
		||||
	} else {
 | 
			
		||||
		g.write('.')
 | 
			
		||||
	}
 | 
			
		||||
	// check for field.name
 | 
			
		||||
	if node.field_expr is ast.SelectorExpr {
 | 
			
		||||
		if node.field_expr.expr is ast.Ident {
 | 
			
		||||
			if node.field_expr.expr.name == g.comp_for_field_var &&
 | 
			
		||||
				node.field_expr.field_name == 'name' {
 | 
			
		||||
				g.write(g.comp_for_field_value.name)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	g.expr(node.field_expr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
 | 
			
		||||
	if node.is_vweb {
 | 
			
		||||
		is_html := node.method_name == 'html'
 | 
			
		||||
| 
						 | 
				
			
			@ -314,17 +334,17 @@ fn (mut g Gen) comp_for(node ast.CompFor) {
 | 
			
		|||
		}
 | 
			
		||||
	} else if node.kind == .fields {
 | 
			
		||||
		// TODO add fields
 | 
			
		||||
		// TODO: temporary, remove this
 | 
			
		||||
		sym_info := sym.info
 | 
			
		||||
		if sym_info is table.Struct {
 | 
			
		||||
			mut fields := sym_info.fields.filter(it.attrs.len == 0)
 | 
			
		||||
			fields_with_attrs := sym_info.fields.filter(it.attrs.len > 0)
 | 
			
		||||
		if sym.info is table.Struct {
 | 
			
		||||
			mut fields := sym.info.fields.filter(it.attrs.len == 0)
 | 
			
		||||
			fields_with_attrs := sym.info.fields.filter(it.attrs.len > 0)
 | 
			
		||||
			fields << fields_with_attrs
 | 
			
		||||
			if fields.len > 0 {
 | 
			
		||||
				g.writeln('\tFieldData $node.val_var;')
 | 
			
		||||
				g.writeln('\tmemset(&$node.val_var, 0, sizeof(FieldData));')
 | 
			
		||||
			}
 | 
			
		||||
			for field in fields {
 | 
			
		||||
				g.comp_for_field_var = node.val_var
 | 
			
		||||
				g.comp_for_field_value = field
 | 
			
		||||
				g.writeln('\t// field $i')
 | 
			
		||||
				g.writeln('\t${node.val_var}.name = _SLIT("$field.name");')
 | 
			
		||||
				if field.attrs.len == 0 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -573,6 +573,9 @@ fn (mut g JsGen) expr(node ast.Expr) {
 | 
			
		|||
		ast.ComptimeCall {
 | 
			
		||||
			// TODO
 | 
			
		||||
		}
 | 
			
		||||
		ast.ComptimeSelector {
 | 
			
		||||
			// TODO
 | 
			
		||||
		}
 | 
			
		||||
		ast.UnsafeExpr {
 | 
			
		||||
			g.expr(node.expr)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -263,37 +263,19 @@ fn os_from_string(os string) pref.OS {
 | 
			
		|||
	return .linux
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// `app.$action()` (`action` is a string)
 | 
			
		||||
// `typ` is `App` in this example
 | 
			
		||||
// fn (mut p Parser) comptime_method_call(typ table.Type) ast.ComptimeCall {
 | 
			
		||||
fn (mut p Parser) comptime_method_call(left ast.Expr) ast.ComptimeCall {
 | 
			
		||||
fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr {
 | 
			
		||||
	p.check(.dollar)
 | 
			
		||||
	mut has_parens := false
 | 
			
		||||
	if p.tok.kind == .lpar {
 | 
			
		||||
		p.check(.lpar)
 | 
			
		||||
		has_parens = true
 | 
			
		||||
	}
 | 
			
		||||
	if p.peek_tok.kind == .lpar {
 | 
			
		||||
		method_name := p.check_name()
 | 
			
		||||
	/*
 | 
			
		||||
	mut j := 0
 | 
			
		||||
	sym := p.table.get_type_symbol(typ)
 | 
			
		||||
	if sym.kind != .struct_ {
 | 
			
		||||
		p.error('not a struct')
 | 
			
		||||
		// `app.$action()` (`action` is a string)
 | 
			
		||||
		if has_parens {
 | 
			
		||||
			p.check(.rpar)
 | 
			
		||||
		}
 | 
			
		||||
	// info := sym.info as table.Struct
 | 
			
		||||
	for method in sym.methods {
 | 
			
		||||
		if method.return_type != table.void_type {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		/*
 | 
			
		||||
		receiver := method.args[0]
 | 
			
		||||
		if !p.expr_var.ptr {
 | 
			
		||||
			p.error('`$p.expr_var.name` needs to be a reference')
 | 
			
		||||
		}
 | 
			
		||||
		amp := if receiver.is_mut && !p.expr_var.ptr { '&' } else { '' }
 | 
			
		||||
		if j > 0 {
 | 
			
		||||
			p.gen(' else ')
 | 
			
		||||
		}
 | 
			
		||||
		p.genln('if (string_eq($method_name, _STR("$method.name")) ) ' + '${typ.name}_$method.name ($amp $p.expr_var.name);')
 | 
			
		||||
		*/
 | 
			
		||||
		j++
 | 
			
		||||
	}
 | 
			
		||||
	*/
 | 
			
		||||
		p.check(.lpar)
 | 
			
		||||
		mut args_var := ''
 | 
			
		||||
		if p.tok.kind == .name {
 | 
			
		||||
| 
						 | 
				
			
			@ -303,13 +285,22 @@ fn (mut p Parser) comptime_method_call(left ast.Expr) ast.ComptimeCall {
 | 
			
		|||
		p.check(.rpar)
 | 
			
		||||
		if p.tok.kind == .key_orelse {
 | 
			
		||||
			p.check(.key_orelse)
 | 
			
		||||
		// p.genln('else {')
 | 
			
		||||
			p.check(.lcbr)
 | 
			
		||||
		// p.statements()
 | 
			
		||||
		}
 | 
			
		||||
		return ast.ComptimeCall{
 | 
			
		||||
			has_parens: has_parens
 | 
			
		||||
			left: left
 | 
			
		||||
			method_name: method_name
 | 
			
		||||
			args_var: args_var
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	expr := p.expr(0)
 | 
			
		||||
	if has_parens {
 | 
			
		||||
		p.check(.rpar)
 | 
			
		||||
	}
 | 
			
		||||
	return ast.ComptimeSelector{
 | 
			
		||||
		has_parens: has_parens
 | 
			
		||||
		left: left
 | 
			
		||||
		field_expr: expr
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1413,7 +1413,7 @@ fn (mut p Parser) scope_register_ab() {
 | 
			
		|||
fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
 | 
			
		||||
	p.next()
 | 
			
		||||
	if p.tok.kind == .dollar {
 | 
			
		||||
		return p.comptime_method_call(left)
 | 
			
		||||
		return p.comptime_selector(left)
 | 
			
		||||
	}
 | 
			
		||||
	is_generic_call := p.is_generic_call()
 | 
			
		||||
	name_pos := p.tok.position()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,62 @@
 | 
			
		|||
struct Foo {
 | 
			
		||||
	immutable int
 | 
			
		||||
mut:
 | 
			
		||||
	test string
 | 
			
		||||
	name string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn comptime_field_selector_read<T>() []string {
 | 
			
		||||
	mut t := T{}
 | 
			
		||||
	t.name = '2'
 | 
			
		||||
	t.test = '1'
 | 
			
		||||
	mut value_list := []string{}
 | 
			
		||||
	$for f in T.fields {
 | 
			
		||||
		$if f.typ is string {
 | 
			
		||||
			value_list << t.$f.name
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return value_list
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_comptime_field_selector_read() {
 | 
			
		||||
	assert comptime_field_selector_read<Foo>() == ['1', '2']
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn comptime_field_selector_write<T>() T {
 | 
			
		||||
	mut t := T{}
 | 
			
		||||
	$for f in T.fields {
 | 
			
		||||
		$if f.typ is string {
 | 
			
		||||
			t.$f.name = '1'
 | 
			
		||||
		}
 | 
			
		||||
		$if f.typ is int {
 | 
			
		||||
			t.$f.name = 1
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_comptime_field_selector_write() {
 | 
			
		||||
	res := comptime_field_selector_write<Foo>()
 | 
			
		||||
	assert res.immutable == 1
 | 
			
		||||
	assert res.test == '1'
 | 
			
		||||
	assert res.name == '1'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Foo2 {
 | 
			
		||||
	f Foo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn nested_with_parentheses<T>() T {
 | 
			
		||||
	mut t := T{}
 | 
			
		||||
	$for f in T.fields {
 | 
			
		||||
		$if f.typ is Foo {
 | 
			
		||||
			t.$(f.name).test = '1'
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return t
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn test_nested_with_parentheses() {
 | 
			
		||||
	res := nested_with_parentheses<Foo2>()
 | 
			
		||||
	assert res.f.test == '1'
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue