1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

cgen: fix map with comp-time reflection, improve comptime var handling (#17603)

This commit is contained in:
Felipe Pena 2023-03-14 08:49:29 -03:00 committed by GitHub
parent 2643d6645f
commit b71c131678
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 407 additions and 70 deletions

View File

@ -872,7 +872,7 @@ fn (t Tree) var(node ast.Var) &Node {
obj.add_terse('is_mut', t.bool_node(node.is_mut))
obj.add('is_used', t.bool_node(node.is_used))
obj.add('is_changed', t.bool_node(node.is_changed))
obj.add('is_comptime_field', t.bool_node(node.is_comptime_field))
obj.add_terse('ct_type_var', t.enum_node(node.ct_type_var))
obj.add('is_or', t.bool_node(node.is_or))
obj.add('is_tmp', t.bool_node(node.is_tmp))
obj.add('is_autofree_tmp', t.bool_node(node.is_autofree_tmp))

View File

@ -649,6 +649,13 @@ pub mut:
types []Type
}
pub enum ComptimeVarKind {
no_comptime // it is not a comptime var
key_var // map key from `for k,v in t.$(field.name)`
value_var // map value from `for k,v in t.$(field.name)`
field_var // comptime field var `a := t.$(field.name)`
}
[minify]
pub struct Var {
pub:
@ -669,11 +676,10 @@ pub mut:
// 10 <- original type (orig_type)
// [11, 12, 13] <- cast order (smartcasts)
// 12 <- the current casted type (typ)
pos token.Pos
is_used bool // whether the local variable was used in other expressions
is_changed bool // to detect mutable vars that are never changed
is_comptime_field bool // comptime field var `a := t.$(field.name)`
//
pos token.Pos
is_used bool // whether the local variable was used in other expressions
is_changed bool // to detect mutable vars that are never changed
ct_type_var ComptimeVarKind // comptime variable type
// (for setting the position after the or block for autofree)
is_or bool // `x := foo() or { ... }`
is_tmp bool // for tmp for loop vars, so that autofree can skip them

View File

@ -122,6 +122,13 @@ pub fn (mut s Scope) update_var_type(name string, typ Type) {
}
}
pub fn (mut s Scope) update_ct_var_kind(name string, kind ComptimeVarKind) {
mut obj := unsafe { s.objects[name] }
if mut obj is Var {
obj.ct_type_var = kind
}
}
// selector_expr: name.field_name
pub fn (mut s Scope) register_struct_field(name string, field ScopeStructField) {
if f := s.struct_fields[name] {

View File

@ -318,14 +318,14 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
}
}
if right is ast.ComptimeSelector {
left.obj.is_comptime_field = true
left.obj.ct_type_var = .field_var
left.obj.typ = c.comptime_fields_default_type
} else if right is ast.Ident
&& (right as ast.Ident).obj is ast.Var && (right as ast.Ident).or_expr.kind == .absent {
left.obj.is_comptime_field = ((right as ast.Ident).obj as ast.Var).is_comptime_field
left.obj.ct_type_var = ((right as ast.Ident).obj as ast.Var).ct_type_var
} else if right is ast.DumpExpr
&& (right as ast.DumpExpr).expr is ast.ComptimeSelector {
left.obj.is_comptime_field = true
left.obj.ct_type_var = .field_var
left.obj.typ = c.comptime_fields_default_type
}
}

View File

@ -98,6 +98,8 @@ mut:
for_in_any_val_type ast.Type
comptime_for_field_var string
comptime_fields_default_type ast.Type
comptime_fields_key_type ast.Type // key type on `$for k, v in val.$(field.name)`
comptime_fields_val_type ast.Type // value type on `$for k, v in val.$(field.name)`
comptime_fields_type map[string]ast.Type
comptime_for_field_value ast.StructField // value of the field variable
comptime_enum_field_value string // current enum value name
@ -2480,8 +2482,12 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type {
c.expected_type = ast.string_type
node.expr_type = c.expr(node.expr)
if node.expr is ast.Ident && c.is_comptime_var(node.expr) {
node.expr_type = c.comptime_fields_default_type
if c.inside_comptime_for_field && node.expr is ast.Ident {
if c.is_comptime_var(node.expr) {
node.expr_type = c.get_comptime_var_type(node.expr as ast.Ident)
} else if (node.expr as ast.Ident).name in c.comptime_fields_type {
node.expr_type = c.comptime_fields_type[(node.expr as ast.Ident).name]
}
}
c.check_expr_opt_call(node.expr, node.expr_type)
etidx := node.expr_type.idx()
@ -3656,7 +3662,7 @@ fn (c &Checker) has_return(stmts []ast.Stmt) ?bool {
pub fn (mut c Checker) is_comptime_var(node ast.Expr) bool {
return c.inside_comptime_for_field && node is ast.Ident
&& (node as ast.Ident).info is ast.IdentVar && (node as ast.Ident).kind == .variable && ((node as ast.Ident).obj as ast.Var).is_comptime_field
&& (node as ast.Ident).info is ast.IdentVar && (node as ast.Ident).kind == .variable && ((node as ast.Ident).obj as ast.Var).ct_type_var != .no_comptime
}
fn (mut c Checker) mark_as_referenced(mut node ast.Expr, as_interface bool) {

View File

@ -10,6 +10,23 @@ import v.util
import v.pkgconfig
import v.checker.constants
[inline]
fn (mut c Checker) get_comptime_var_type(node ast.Expr) ast.Type {
if node is ast.Ident && (node as ast.Ident).obj is ast.Var {
return match (node.obj as ast.Var).ct_type_var {
.key_var { c.comptime_fields_key_type }
.value_var { c.comptime_fields_val_type }
.field_var { c.comptime_fields_default_type }
else { ast.void_type }
}
} else if node is ast.ComptimeSelector {
return c.get_comptime_selector_type(node, ast.void_type)
} else if node is ast.SelectorExpr && c.is_comptime_selector_type(node as ast.SelectorExpr) {
return c.comptime_fields_default_type
}
return ast.void_type
}
fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type {
if node.left !is ast.EmptyExpr {
node.left_type = c.expr(node.left)
@ -606,6 +623,16 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) ComptimeBran
} else if cond.left in [ast.Ident, ast.SelectorExpr, ast.TypeNode] {
// `$if method.@type is string`
c.expr(cond.left)
if cond.left is ast.SelectorExpr
&& c.is_comptime_selector_type(cond.left as ast.SelectorExpr)
&& cond.right is ast.ComptimeType {
checked_type := c.get_comptime_var_type(cond.left)
return if c.table.is_comptime_type(checked_type, cond.right) {
.eval
} else {
.skip
}
}
return .unknown
} else {
c.error('invalid `\$if` condition: expected a type or a selector expression or an interface check',
@ -818,6 +845,15 @@ fn (mut c Checker) get_comptime_selector_type(node ast.ComptimeSelector, default
return default_type
}
// check_comptime_is_field_selector checks if the SelectorExpr is related to $for variable
[inline]
fn (mut c Checker) is_comptime_selector_type(node ast.SelectorExpr) bool {
if c.inside_comptime_for_field && node.expr is ast.Ident {
return (node.expr as ast.Ident).name == c.comptime_for_field_var && node.field_name == 'typ'
}
return false
}
// check_comptime_is_field_selector checks if the SelectorExpr is related to $for variable
[inline]
fn (mut c Checker) check_comptime_is_field_selector(node ast.SelectorExpr) bool {

View File

@ -81,6 +81,12 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
node.high_type = high_type
node.scope.update_var_type(node.val_var, node.val_type)
} else {
mut is_comptime := false
if (node.cond is ast.Ident && c.is_comptime_var(node.cond))
|| node.cond is ast.ComptimeSelector {
is_comptime = true
typ = c.unwrap_generic(c.comptime_fields_default_type)
}
mut sym := c.table.final_sym(typ)
if sym.kind != .string {
match mut node.cond {
@ -147,11 +153,21 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
}
node.key_type = key_type
node.scope.update_var_type(node.key_var, key_type)
if is_comptime {
c.comptime_fields_key_type = key_type
node.scope.update_ct_var_kind(node.key_var, .key_var)
}
}
value_type := c.table.value_type(unwrapped_typ)
node.scope.update_var_type(node.val_var, value_type)
if is_comptime {
c.comptime_fields_val_type = value_type
node.scope.update_ct_var_kind(node.val_var, .value_var)
}
c.inside_for_in_any_cond = true
c.for_in_any_val_type = value_type
} else {
@ -167,6 +183,11 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
}
node.key_type = key_type
node.scope.update_var_type(node.key_var, key_type)
if is_comptime {
c.comptime_fields_key_type = key_type
node.scope.update_ct_var_kind(node.key_var, .key_var)
}
}
mut value_type := c.table.value_type(typ)
if sym.kind == .string {
@ -216,6 +237,10 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
node.kind = sym.kind
node.val_type = value_type
node.scope.update_var_type(node.val_var, value_type)
if is_comptime {
c.comptime_fields_val_type = value_type
node.scope.update_ct_var_kind(node.val_var, .value_var)
}
}
}
c.check_loop_label(node.label, node.pos)

View File

@ -222,9 +222,8 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
}
}
if mut left.obj is ast.Var {
if val is ast.Ident
&& (val as ast.Ident).info is ast.IdentVar && (val as ast.Ident).kind == .variable && (val as ast.Ident).obj is ast.Var && ((val as ast.Ident).obj as ast.Var).is_comptime_field {
var_type = g.unwrap_generic(g.comptime_for_field_type)
if val is ast.Ident && g.is_comptime_var(val) {
var_type = g.unwrap_generic(g.get_comptime_var_type(val))
val_type = var_type
gen_or = val.or_expr.kind != .absent
if gen_or {

View File

@ -199,8 +199,10 @@ mut:
comptime_for_method_var string // $for method in T.methods {}; the variable name
comptime_for_field_var string // $for field in T.fields {}; the variable name
comptime_for_field_value ast.StructField // value of the field variable
comptime_enum_field_value string // value of enum name
comptime_for_field_type ast.Type // type of the field variable inferred from `$if field.typ is T {}`
comptime_for_field_type ast.Type // type of the field variable inferred from `$if field.typ is T {}`
comptime_for_field_key_type ast.Type // type of key on comptime for on map field
comptime_for_field_val_type ast.Type // type of value on comptime for on map field
comptime_enum_field_value string // value of enum name
comptime_var_type_map map[string]ast.Type
comptime_values_stack []CurrentComptimeValues // stores the values from the above on each $for loop, to make nesting them easier
prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type
@ -3168,7 +3170,7 @@ fn (mut g Gen) expr(node_ ast.Expr) {
}
}
ast.IsRefType {
typ := g.get_type(node.typ)
typ := g.resolve_comptime_type(node.expr, g.get_type(node.typ))
node_typ := g.unwrap_generic(typ)
sym := g.table.sym(node_typ)
if sym.language == .v && sym.kind in [.placeholder, .any] {
@ -3376,7 +3378,7 @@ fn (mut g Gen) type_name(raw_type ast.Type) {
}
fn (mut g Gen) typeof_expr(node ast.TypeOf) {
typ := g.get_type(node.typ)
typ := g.resolve_comptime_type(node.expr, g.get_type(node.typ))
sym := g.table.sym(typ)
if sym.kind == .sum_type {
// When encountering a .sum_type, typeof() should be done at runtime,
@ -3399,20 +3401,6 @@ fn (mut g Gen) typeof_expr(node ast.TypeOf) {
}
}
fn (mut g Gen) comptime_typeof(node ast.TypeOf, default_type ast.Type) ast.Type {
if node.expr is ast.ComptimeSelector {
key_str := g.get_comptime_selector_key_type(node.expr)
if key_str != '' {
return g.comptime_var_type_map[key_str] or { default_type }
}
} else if g.inside_comptime_for_field && node.expr is ast.Ident
&& (node.expr as ast.Ident).obj is ast.Var && ((node.expr as ast.Ident).obj as ast.Var).is_comptime_field == true {
// typeof(var) from T.fields
return g.comptime_for_field_type
}
return default_type
}
fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once
g.prevent_sum_type_unwrapping_once = false
@ -3432,14 +3420,14 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
// typeof(expr).name
mut name_type := node.name_type
if node.expr is ast.TypeOf {
name_type = g.comptime_typeof(node.expr, name_type)
name_type = g.resolve_comptime_type(node.expr.expr, name_type)
}
g.type_name(name_type)
return
} else if node.field_name == 'idx' {
mut name_type := node.name_type
if node.expr is ast.TypeOf {
name_type = g.comptime_typeof(node.expr, name_type)
name_type = g.resolve_comptime_type(node.expr.expr, name_type)
}
// `typeof(expr).idx`
g.write(int(g.unwrap_generic(name_type)).str())
@ -4076,7 +4064,7 @@ fn (mut g Gen) select_expr(node ast.SelectExpr) {
pub fn (mut g Gen) is_comptime_var(node ast.Expr) bool {
return g.inside_comptime_for_field && node is ast.Ident
&& (node as ast.Ident).info is ast.IdentVar && ((node as ast.Ident).obj as ast.Var).is_comptime_field
&& (node as ast.Ident).info is ast.IdentVar && ((node as ast.Ident).obj as ast.Var).ct_type_var != .no_comptime
}
fn (mut g Gen) ident(node ast.Ident) {
@ -4109,13 +4097,14 @@ fn (mut g Gen) ident(node ast.Ident) {
mut is_auto_heap := false
if node.info is ast.IdentVar {
if node.obj is ast.Var {
if !g.is_assign_lhs && node.obj.is_comptime_field {
if g.comptime_for_field_type.has_flag(.option) {
if !g.is_assign_lhs && node.obj.ct_type_var != .no_comptime {
comptime_type := g.get_comptime_var_type(node)
if comptime_type.has_flag(.option) {
if (g.inside_opt_or_res || g.left_is_opt) && node.or_expr.kind == .absent {
g.write('${name}')
} else {
g.write('/*opt*/')
styp := g.base_type(g.comptime_for_field_type)
styp := g.base_type(comptime_type)
g.write('(*(${styp}*)${name}.data)')
}
} else {
@ -4125,7 +4114,7 @@ fn (mut g Gen) ident(node ast.Ident) {
&& !g.is_assign_lhs) {
stmt_str := g.go_before_stmt(0).trim_space()
g.empty_line = true
g.or_block(name, node.or_expr, g.comptime_for_field_type)
g.or_block(name, node.or_expr, comptime_type)
g.writeln(stmt_str)
}
return
@ -4234,7 +4223,7 @@ fn (mut g Gen) cast_expr(node ast.CastExpr) {
mut expr_type := node.expr_type
sym := g.table.sym(node_typ)
if node.expr is ast.Ident && g.is_comptime_var(node.expr) {
expr_type = g.unwrap_generic(g.comptime_for_field_type)
expr_type = g.unwrap_generic(g.get_comptime_var_type(node.expr))
}
if sym.kind in [.sum_type, .interface_] {
if node.typ.has_flag(.option) && node.expr is ast.None {
@ -6161,7 +6150,7 @@ fn (mut g Gen) get_type(typ ast.Type) ast.Type {
}
fn (mut g Gen) size_of(node ast.SizeOf) {
typ := g.get_type(node.typ)
typ := g.resolve_comptime_type(node.expr, g.get_type(node.typ))
node_typ := g.unwrap_generic(typ)
sym := g.table.sym(node_typ)
if sym.language == .v && sym.kind in [.placeholder, .any] {

View File

@ -647,17 +647,29 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) (bool, bool) {
//
struct CurrentComptimeValues {
inside_comptime_for_field bool
comptime_for_method string
comptime_for_method_var string
comptime_for_field_var string
comptime_for_field_value ast.StructField
comptime_for_field_type ast.Type
comptime_var_type_map map[string]ast.Type
inside_comptime_for_field bool
comptime_for_method string
comptime_for_method_var string
comptime_for_field_var string
comptime_for_field_value ast.StructField
comptime_for_field_type ast.Type
comptime_for_field_key_type ast.Type
comptime_for_field_val_type ast.Type
comptime_var_type_map map[string]ast.Type
}
fn (mut g Gen) push_existing_comptime_values() {
g.comptime_values_stack << CurrentComptimeValues{g.inside_comptime_for_field, g.comptime_for_method, g.comptime_for_method_var, g.comptime_for_field_var, g.comptime_for_field_value, g.comptime_for_field_type, g.comptime_var_type_map.clone()}
g.comptime_values_stack << CurrentComptimeValues{
inside_comptime_for_field: g.inside_comptime_for_field
comptime_for_method: g.comptime_for_method
comptime_for_method_var: g.comptime_for_method_var
comptime_for_field_var: g.comptime_for_field_var
comptime_for_field_value: g.comptime_for_field_value
comptime_for_field_type: g.comptime_for_field_type
comptime_for_field_key_type: g.comptime_for_field_key_type
comptime_for_field_val_type: g.comptime_for_field_val_type
comptime_var_type_map: g.comptime_var_type_map.clone()
}
}
fn (mut g Gen) pop_existing_comptime_values() {
@ -668,9 +680,40 @@ fn (mut g Gen) pop_existing_comptime_values() {
g.comptime_for_field_var = old.comptime_for_field_var
g.comptime_for_field_value = old.comptime_for_field_value
g.comptime_for_field_type = old.comptime_for_field_type
g.comptime_for_field_key_type = old.comptime_for_field_key_type
g.comptime_for_field_val_type = old.comptime_for_field_val_type
g.comptime_var_type_map = old.comptime_var_type_map.clone()
}
[inline]
fn (mut g Gen) get_comptime_var_type_from_kind(kind ast.ComptimeVarKind) ast.Type {
return match kind {
.key_var { g.comptime_for_field_key_type }
.value_var { g.comptime_for_field_val_type }
.field_var { g.comptime_for_field_type }
else { ast.void_type }
}
}
fn (mut g Gen) get_comptime_var_type(node ast.Expr) ast.Type {
if node is ast.Ident && (node as ast.Ident).obj is ast.Var {
return g.get_comptime_var_type_from_kind((node.obj as ast.Var).ct_type_var)
} else if node is ast.ComptimeSelector {
key_str := g.get_comptime_selector_key_type(node)
if key_str != '' {
return g.comptime_var_type_map[key_str] or { ast.void_type }
}
}
return ast.void_type
}
fn (mut g Gen) resolve_comptime_type(node ast.Expr, default_type ast.Type) ast.Type {
if (node is ast.Ident && g.is_comptime_var(node)) || node is ast.ComptimeSelector {
return g.get_comptime_var_type(node)
}
return default_type
}
//
fn (mut g Gen) comptime_for(node ast.ComptimeFor) {

View File

@ -37,8 +37,8 @@ fn (mut g Gen) dump_expr(node ast.DumpExpr) {
}
}
}
} else if node.expr is ast.Ident && g.is_comptime_var(node.expr) {
expr_type = g.comptime_for_field_type
} else if node.expr is ast.Ident && g.inside_comptime_for_field && g.is_comptime_var(node.expr) {
expr_type = g.get_comptime_var_type(node.expr)
name = g.typ(g.unwrap_generic(expr_type.clear_flag(.shared_f).clear_flag(.result))).replace('*',
'')
}

View File

@ -883,8 +883,8 @@ fn (mut g Gen) gen_to_str_method_call(node ast.CallExpr) bool {
}
} else if left_node is ast.Ident {
if left_node.obj is ast.Var {
if left_node.obj.is_comptime_field {
rec_type = g.comptime_for_field_type
if left_node.obj.ct_type_var != .no_comptime {
rec_type = g.get_comptime_var_type(left_node)
g.gen_expr_to_string(left_node, rec_type)
return true
} else if g.comptime_var_type_map.len > 0 {
@ -912,18 +912,18 @@ fn (mut g Gen) gen_to_str_method_call(node ast.CallExpr) bool {
return false
}
fn (mut g Gen) change_comptime_args(mut node_ ast.CallExpr) []int {
mut comptime_args := []int{}
fn (mut g Gen) change_comptime_args(mut node_ ast.CallExpr) map[int]ast.Type {
mut comptime_args := map[int]ast.Type{}
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
if call_arg.expr.obj.is_comptime_field {
comptime_args << i
if call_arg.expr.obj.ct_type_var != .no_comptime {
comptime_args[i] = g.get_comptime_var_type_from_kind(call_arg.expr.obj.ct_type_var)
}
}
} else if mut call_arg.expr is ast.ComptimeSelector {
comptime_args << i
comptime_args[i] = g.comptime_for_field_type
}
}
return comptime_args
@ -940,7 +940,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
left_type := g.unwrap_generic(node.left_type)
mut unwrapped_rec_type := node.receiver_type
mut for_in_any_var_type := ast.void_type
mut comptime_args := []int{}
mut comptime_args := map[int]ast.Type{}
if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0 { // in generic fn
unwrapped_rec_type = g.unwrap_generic(node.receiver_type)
} else { // in non-generic fn
@ -1151,13 +1151,13 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
mut concrete_types := node.concrete_types.map(g.unwrap_generic(it))
arg_sym := g.table.sym(g.comptime_for_field_type)
if m := g.table.find_method(g.table.sym(node.left_type), node.name) {
for k in comptime_args {
for k, v in comptime_args {
if m.generic_names.len > 0 && arg_sym.kind == .array
&& m.params[k + 1].typ.has_flag(.generic)
&& g.table.final_sym(m.params[k + 1].typ).kind == .array {
concrete_types[k] = (arg_sym.info as ast.Array).elem_type
} else {
concrete_types[k] = g.comptime_for_field_type
concrete_types[k] = v
}
}
}
@ -1288,7 +1288,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
// will be `0` for `foo()`
mut is_interface_call := false
mut is_selector_call := false
mut comptime_args := []int{}
mut comptime_args := map[int]ast.Type{}
if node.left_type != 0 {
left_sym := g.table.sym(node.left_type)
if left_sym.kind == .interface_ {
@ -1408,12 +1408,12 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
&& comptime_args.len > 0 {
mut concrete_types := node.concrete_types.map(g.unwrap_generic(it))
arg_sym := g.table.sym(g.comptime_for_field_type)
for k in comptime_args {
for k, v in comptime_args {
if arg_sym.kind == .array && func.params[k].typ.has_flag(.generic)
&& g.table.sym(func.params[k].typ).kind == .array {
concrete_types[k] = (arg_sym.info as ast.Array).elem_type
} else {
concrete_types[k] = g.comptime_for_field_type
concrete_types[k] = v
}
}
name = g.generic_fn_name(concrete_types, name)

View File

@ -130,17 +130,40 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) {
mut node := unsafe { node_ }
if node.kind == .any {
mut is_comptime := false
if (node.cond is ast.Ident && g.is_comptime_var(node.cond)) || node.cond is ast.ComptimeSelector {
is_comptime = true
mut unwrapped_typ := g.unwrap_generic(g.comptime_for_field_type)
mut unwrapped_sym := g.table.sym(unwrapped_typ)
node.cond_type = unwrapped_typ
node.val_type = g.table.value_type(unwrapped_typ)
node.scope.update_var_type(node.val_var, node.val_type)
node.kind = unwrapped_sym.kind
g.comptime_for_field_val_type = node.val_type
node.scope.update_ct_var_kind(node.val_var, .value_var)
if node.key_var.len > 0 {
key_type := match unwrapped_sym.kind {
.map { unwrapped_sym.map_info().key_type }
else { ast.int_type }
}
node.key_type = key_type
node.scope.update_var_type(node.key_var, key_type)
g.comptime_for_field_key_type = node.key_type
node.scope.update_ct_var_kind(node.key_var, .key_var)
}
}
if node.kind == .any && !is_comptime {
g.inside_for_in_any_cond = true
mut unwrapped_typ := g.unwrap_generic(node.cond_type)
mut unwrapped_sym := g.table.sym(unwrapped_typ)
node.kind = unwrapped_sym.kind
node.cond_type = unwrapped_typ
if node.key_var.len > 0 {
if g.is_comptime_var(node.cond) {
unwrapped_typ = g.unwrap_generic(g.comptime_for_field_type)
unwrapped_sym = g.table.sym(unwrapped_typ)
}
key_type := match unwrapped_sym.kind {
.map { unwrapped_sym.map_info().key_type }
else { ast.int_type }

View File

@ -0,0 +1,203 @@
struct StructType {
mut:
val string = 'string from StructType'
}
struct Data {
mut:
not_map string
empty_to_test map[string]string
users map[string]StructType
extra map[string]map[string]int
}
fn test_comptimeselector_map_different_types() {
data := Data{
users: {
'a': StructType{}
}
extra: {
'b': {
'c': 10
}
}
}
mut keys := []string{}
mut vals := []string{}
$for field in Data.fields {
$if field.typ is $Map {
for k, v in data.$(field.name) {
keys << k.str()
vals << v.str()
}
}
}
assert keys == ['a', 'b']
assert vals[0] == "StructType{
val: 'string from StructType'
}"
assert vals[1] == "{'c': 10}"
}
fn test_comptime_var_map_different_types() {
data := Data{
users: {
'a': StructType{}
}
extra: {
'b': {
'c': 10
}
}
}
mut keys := []string{}
mut vals := []string{}
$for field in Data.fields {
$if field.typ is $Map {
gg := data.$(field.name)
for k, v in gg {
keys << k.str()
vals << v.str()
}
}
}
assert keys == ['a', 'b']
assert vals[0] == "StructType{
val: 'string from StructType'
}"
assert vals[1] == "{'c': 10}"
}
fn test_comptime_with_dump() {
data := Data{
users: {
'a': StructType{}
}
extra: {
'b': {
'c': 10
}
}
}
$for field in Data.fields {
$if field.typ is $Map {
for k, v in data.$(field.name) {
dump(k)
dump(v)
}
}
}
assert true
}
fn test_comptime_dump_for_key_and_value() {
data := Data{
users: {
'a': StructType{}
}
extra: {
'b': {
'c': 10
}
}
}
mut key_types := []string{}
mut val_types := []string{}
$for field in Data.fields {
$if field.typ is $Map {
for k, v in data.$(field.name) {
key_types << typeof(k).name
val_types << typeof(v).name
}
}
}
assert val_types[0] == 'StructType'
assert val_types[1] == 'map[string]int'
assert key_types[0] == 'string'
assert key_types[1] == 'string'
}
fn test_comptime_key_value_var() {
data := Data{
users: {
'a': StructType{}
}
extra: {
'b': {
'c': 10
}
}
}
mut keys := []string{}
mut vals := []string{}
$for field in Data.fields {
$if field.typ is $Map {
for k, v in data.$(field.name) {
if k == 'a' {
keys << dump(k)
vals << dump(v.str())
}
if k == 'b' {
keys << dump(k)
vals << dump(v.str())
}
}
}
}
assert keys[0] == 'a'
assert keys[1] == 'b'
assert vals[0] == "StructType{
val: 'string from StructType'
}"
assert vals[1] == "{'c': 10}"
}
fn test_comptime_generic_argument() {
data := Data{
users: {
'a': StructType{}
}
extra: {
'b': {
'c': 10
}
}
}
$for field in Data.fields {
$if field.typ is $Map {
for k, v in data.$(field.name) {
process_value_from_map(v)
assert k in ['a', 'b']
}
}
}
}
fn process_value_from_map[T](v T) {
$if T is $Map {
assert typeof(v).name == 'map[string]int'
assert v.str() == "{'c': 10}"
assert true
return
} $else $if T is $Struct {
assert typeof(v).name == 'StructType'
assert v.str() == "StructType{
val: 'string from StructType'
}"
assert true
return
}
assert false
}