diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 5aee25941c..5f26176bf7 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -385,7 +385,7 @@ pub fn (x Expr) str() string { } } ComptimeSelector { - return '${x.left}.$${x.field_expr}' + return '${x.left}.$(${x.field_expr})' } ConcatExpr { return x.vals.map(it.str()).join(',') diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index b76215efb0..778d822bbc 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -296,6 +296,7 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { } if right is ast.ComptimeSelector { left.obj.is_comptime_field = true + left.obj.typ = c.comptime_fields_default_type } } ast.GlobalField { diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 9a8e9ad61b..0138804100 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -873,19 +873,12 @@ fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr) { typ = ast.new_type(idx).derive(arg.typ) } else if c.inside_comptime_for_field && sym.kind in [.struct_, .any] && arg.expr is ast.ComptimeSelector { - compselector := arg.expr as ast.ComptimeSelector - if compselector.field_expr is ast.SelectorExpr { - selectorexpr := compselector.field_expr as ast.SelectorExpr - if selectorexpr.expr is ast.Ident { - ident := selectorexpr.expr as ast.Ident - if ident.name == c.comptime_for_field_var { - typ = c.comptime_fields_default_type - - if func.return_type.has_flag(.generic) - && gt_name == c.table.type_to_str(func.return_type) { - node.comptime_ret_val = true - } - } + comptime_typ := c.get_comptime_selector_type(arg.expr, ast.void_type) + if comptime_typ != ast.void_type { + typ = comptime_typ + if func.return_type.has_flag(.generic) + && gt_name == c.table.type_to_str(func.return_type) { + node.comptime_ret_val = true } } } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 9e27739fd0..86f8e7f2c5 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2688,15 +2688,6 @@ pub fn (mut c Checker) expr(node_ ast.Expr) ast.Type { // return ast.void_type // } -fn (mut c Checker) get_comptime_selector_type(node ast.ComptimeSelector, default_type ast.Type) ast.Type { - if node.field_expr is ast.SelectorExpr - && c.check_comptime_is_field_selector(node.field_expr as ast.SelectorExpr) - && (node.field_expr as ast.SelectorExpr).field_name == 'name' { - return c.comptime_fields_default_type - } - return default_type -} - fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { // Given: `Outside( Inside(xyz) )`, // node.expr_type: `Inside` diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 95263a6b93..f22902b172 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -146,7 +146,7 @@ fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { fn (mut c Checker) comptime_selector(mut node ast.ComptimeSelector) ast.Type { node.left_type = c.expr(node.left) - expr_type := c.unwrap_generic(c.expr(node.field_expr)) + mut expr_type := c.unwrap_generic(c.expr(node.field_expr)) expr_sym := c.table.sym(expr_type) if expr_type != ast.string_type { c.error('expected `string` instead of `${expr_sym.name}` (e.g. `field.name`)', @@ -158,6 +158,10 @@ fn (mut c Checker) comptime_selector(mut node ast.ComptimeSelector) ast.Type { c.error('compile time field access can only be used when iterating over `T.fields`', left_pos) } + expr_type = c.get_comptime_selector_type(node, ast.void_type) + if expr_type != ast.void_type { + return expr_type + } expr_name := node.field_expr.expr.str() if expr_name in c.comptime_fields_type { return c.comptime_fields_type[expr_name] @@ -755,6 +759,18 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) ComptimeBran return .unknown } +// get_comptime_selector_type retrieves the var.$(field.name) type when field_name is 'name' otherwise default_type is returned +[inline] +fn (mut c Checker) get_comptime_selector_type(node ast.ComptimeSelector, default_type ast.Type) ast.Type { + if node.field_expr is ast.SelectorExpr + && c.check_comptime_is_field_selector(node.field_expr as ast.SelectorExpr) + && (node.field_expr as ast.SelectorExpr).field_name == 'name' { + return c.unwrap_generic(c.comptime_fields_default_type) + } + return default_type +} + +// 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 { if c.inside_comptime_for_field && node.expr is ast.Ident { @@ -763,6 +779,7 @@ fn (mut c Checker) check_comptime_is_field_selector(node ast.SelectorExpr) bool return false } +// check_comptime_is_field_selector_bool checks if the SelectorExpr is related to field.is_* boolean fields [inline] fn (mut c Checker) check_comptime_is_field_selector_bool(node ast.SelectorExpr) bool { if c.check_comptime_is_field_selector(node) { @@ -772,6 +789,7 @@ fn (mut c Checker) check_comptime_is_field_selector_bool(node ast.SelectorExpr) return false } +// get_comptime_selector_bool_field evaluates the bool value for field.is_* fields fn (mut c Checker) get_comptime_selector_bool_field(field_name string) bool { field := c.comptime_for_field_value field_typ := c.comptime_fields_default_type diff --git a/vlib/v/checker/for.v b/vlib/v/checker/for.v index 20a3e6749d..7bc025d6d9 100644 --- a/vlib/v/checker/for.v +++ b/vlib/v/checker/for.v @@ -37,15 +37,6 @@ fn (mut c Checker) for_c_stmt(node ast.ForCStmt) { c.in_for_count-- } -fn (mut c Checker) get_compselector_type_from_selector_name(node ast.ComptimeSelector) (bool, ast.Type) { - if c.inside_comptime_for_field && node.field_expr is ast.SelectorExpr - && (node.field_expr as ast.SelectorExpr).expr.str() in c.comptime_fields_type - && (node.field_expr as ast.SelectorExpr).field_name == 'name' { - return true, c.unwrap_generic(c.comptime_fields_default_type) - } - return false, ast.void_type -} - fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) { c.in_for_count++ prev_loop_label := c.loop_label @@ -97,10 +88,10 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) { node.val_is_ref = node.cond.op == .amp } ast.ComptimeSelector { - found, selector_type := c.get_compselector_type_from_selector_name(node.cond) - if found { - sym = c.table.final_sym(selector_type) - typ = selector_type + comptime_typ := c.get_comptime_selector_type(node.cond, ast.void_type) + if comptime_typ != ast.void_type { + sym = c.table.final_sym(comptime_typ) + typ = comptime_typ } } ast.Ident { diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index abe9719472..6049186248 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -6,6 +6,22 @@ import v.ast import v.pref import v.token +fn (mut c Checker) check_compatible_types(left_type ast.Type, right ast.TypeNode) ComptimeBranchSkipState { + right_type := c.unwrap_generic(right.typ) + sym := c.table.sym(right_type) + + if sym.kind == .interface_ { + checked_type := c.unwrap_generic(left_type) + return if c.table.does_type_implement_interface(checked_type, right_type) { + .eval + } else { + .skip + } + } else { + return if left_type == right_type { .eval } else { .skip } + } +} + fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { if_kind := if node.is_comptime { '\$if' } else { 'if' } mut node_is_expr := false @@ -121,22 +137,25 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { comptime_field_name = left.expr.str() c.comptime_fields_type[comptime_field_name] = got_type is_comptime_type_is_expr = true - } else if right is ast.TypeNode && left is ast.TypeNode - && sym.kind == .interface_ { - is_comptime_type_is_expr = true - // is interface - checked_type := c.unwrap_generic(left.typ) - skip_state = if c.table.does_type_implement_interface(checked_type, - got_type) - { - .eval - } else { - .skip + if comptime_field_name == c.comptime_for_field_var { + left_type := c.unwrap_generic(c.comptime_fields_default_type) + if left.field_name == 'typ' { + skip_state = c.check_compatible_types(left_type, right as ast.TypeNode) + } else if left.field_name == 'unaliased_typ' { + skip_state = c.check_compatible_types(c.table.unaliased_type(left_type), + right as ast.TypeNode) + } + } else if c.check_comptime_is_field_selector_bool(left) { + skip_state = if c.get_comptime_selector_bool_field(left.field_name) { + .eval + } else { + .skip + } } } else if left is ast.TypeNode { is_comptime_type_is_expr = true left_type := c.unwrap_generic(left.typ) - skip_state = if left_type == got_type { .eval } else { .skip } + skip_state = c.check_compatible_types(left_type, right as ast.TypeNode) } } } diff --git a/vlib/v/checker/postfix.v b/vlib/v/checker/postfix.v index 85e5c82311..6ebaa21ecb 100644 --- a/vlib/v/checker/postfix.v +++ b/vlib/v/checker/postfix.v @@ -24,9 +24,7 @@ fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) ast.Type { } if !(typ_sym.is_number() || ((c.inside_unsafe || c.pref.translated) && is_non_void_pointer)) { if c.inside_comptime_for_field { - if c.is_comptime_var(node.expr) { - return c.comptime_fields_default_type - } else if node.expr is ast.ComptimeSelector { + if c.is_comptime_var(node.expr) || node.expr is ast.ComptimeSelector { return c.comptime_fields_default_type } } diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 62ace6a029..f2154de339 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -647,8 +647,7 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini node.update_expr_type = update_type if node.update_expr is ast.ComptimeSelector { c.error('cannot use struct update syntax in compile time expressions', node.update_expr_pos) - } - if c.table.final_sym(update_type).kind != .struct_ { + } else if c.table.final_sym(update_type).kind != .struct_ { s := c.table.type_to_str(update_type) c.error('expected struct, found `${s}`', node.update_expr.pos()) } else if update_type != node.typ { diff --git a/vlib/v/checker/tests/comptime_dump_fields_var_test.out b/vlib/v/checker/tests/comptime_dump_fields_var_test.out index ce59179bc7..28e324695b 100644 --- a/vlib/v/checker/tests/comptime_dump_fields_var_test.out +++ b/vlib/v/checker/tests/comptime_dump_fields_var_test.out @@ -1,8 +1,8 @@ -[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$field.name: c -[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$field.name: d -[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$field.name: 1 -[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$field.name: 0 -[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$field.name: struct { +[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$(field.name): c +[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$(field.name): d +[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$(field.name): 1 +[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$(field.name): 0 +[vlib/v/checker/tests/comptime_dump_fields_var_test.vv:13] val.$(field.name): struct { i: 100 } ok diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index c20163201e..84bed5766a 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -131,6 +131,7 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { key_str := g.get_comptime_selector_key_type(val) if key_str != '' { var_type = g.comptime_var_type_map[key_str] or { var_type } + val_type = var_type left.obj.typ = var_type is_comptime_var = true } @@ -142,6 +143,17 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { } is_auto_heap = left.obj.is_auto_heap } + } else if mut left is ast.ComptimeSelector { + key_str := g.get_comptime_selector_key_type(left) + if key_str != '' { + var_type = g.comptime_var_type_map[key_str] or { var_type } + } + if val is ast.ComptimeSelector { + key_str_right := g.get_comptime_selector_key_type(val) + if key_str_right != '' { + val_type = g.comptime_var_type_map[key_str_right] or { var_type } + } + } } mut styp := g.typ(var_type) mut is_fixed_array_init := false @@ -490,8 +502,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { } else if val_type.has_flag(.shared_f) { g.expr_with_cast(val, val_type, var_type) } else if is_comptime_var && g.right_is_opt { - tmp_var := g.new_tmp_var() - g.expr_with_tmp_var(val, val_type, var_type, tmp_var) + if var_type.has_flag(.option) && val is ast.ComptimeSelector { + g.expr(val) + } else { + tmp_var := g.new_tmp_var() + g.expr_with_tmp_var(val, val_type, var_type, tmp_var) + } } else { g.expr(val) } diff --git a/vlib/x/json2/encoder.v b/vlib/x/json2/encoder.v index 23798a0f69..6b8c1c6378 100644 --- a/vlib/x/json2/encoder.v +++ b/vlib/x/json2/encoder.v @@ -320,7 +320,7 @@ fn (e &Encoder) encode_struct[U](val U, level int, mut wr io.Writer) ! { $if field.unaliased_typ is string { e.encode_string(val.$(field.name).str(), mut wr)! } $else $if field.unaliased_typ is time.Time { - parsed_time := val.$(field.name) as time.Time + parsed_time := time.parse(val.$(field.name).str()) or { time.Time{} } e.encode_string(parsed_time.format_rfc3339(), mut wr)! } $else $if field.unaliased_typ in [bool, $Float, $Int] { wr.write(val.$(field.name).str().bytes())!