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:
parent
2643d6645f
commit
b71c131678
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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] {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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] {
|
||||
|
@ -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) {
|
||||
|
@ -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('*',
|
||||
'')
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 }
|
||||
|
203
vlib/v/tests/comptime_map_test.v
Normal file
203
vlib/v/tests/comptime_map_test.v
Normal 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
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user