diff --git a/vlib/v/checker/assign.v b/vlib/v/checker/assign.v index 6bbbc20cb0..55e8b67cf5 100644 --- a/vlib/v/checker/assign.v +++ b/vlib/v/checker/assign.v @@ -187,6 +187,11 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { && c.table.sym(left_type).kind in [.array, .map, .struct] } if c.comptime.comptime_for_field_var != '' && mut left is ast.ComptimeSelector { + if c.comptime.has_different_types && node.right[i].is_literal() + && !c.comptime.inside_comptime_if { + c.error('mismatched types: check field type with \$if to avoid this problem', + node.right[i].pos()) + } left_type = c.comptime.comptime_for_field_type c.expected_type = c.unwrap_generic(left_type) } diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index e25440191a..dac0ba8d3f 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -271,6 +271,8 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { return } } + has_different_types := fields.len > 1 + && !fields.all(c.check_basic(it.typ, fields[0].typ)) for field in fields { c.push_new_comptime_info() c.comptime.inside_comptime_for = true @@ -283,6 +285,7 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { c.type_resolver.update_ct_type(node.val_var, c.field_data_type) c.type_resolver.update_ct_type('${node.val_var}.typ', node.typ) c.comptime.comptime_for_field_type = field.typ + c.comptime.has_different_types = has_different_types c.stmts(mut node.stmts) unwrapped_expr_type := c.unwrap_generic(field.typ) @@ -809,6 +812,7 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, pos token.Pos) ComptimeBr } else if cond.left in [ast.Ident, ast.SelectorExpr, ast.TypeNode] { // `$if method.type is string` c.expr(mut cond.left) + c.comptime.inside_comptime_if = true if mut cond.left is ast.SelectorExpr && cond.right is ast.ComptimeType { comptime_type := cond.right as ast.ComptimeType if c.comptime.is_comptime_selector_type(cond.left) { @@ -1075,6 +1079,8 @@ fn (mut c Checker) push_new_comptime_info() { c.type_resolver.info_stack << type_resolver.ResolverInfo{ saved_type_map: c.type_resolver.type_map.clone() inside_comptime_for: c.comptime.inside_comptime_for + inside_comptime_if: c.comptime.inside_comptime_if + has_different_types: c.comptime.has_different_types comptime_for_variant_var: c.comptime.comptime_for_variant_var comptime_for_field_var: c.comptime.comptime_for_field_var comptime_for_field_type: c.comptime.comptime_for_field_type @@ -1091,6 +1097,8 @@ fn (mut c Checker) pop_comptime_info() { old := c.type_resolver.info_stack.pop() c.type_resolver.type_map = old.saved_type_map.clone() c.comptime.inside_comptime_for = old.inside_comptime_for + c.comptime.inside_comptime_if = old.inside_comptime_if + c.comptime.has_different_types = old.has_different_types c.comptime.comptime_for_variant_var = old.comptime_for_variant_var c.comptime.comptime_for_field_var = old.comptime_for_field_var c.comptime.comptime_for_field_type = old.comptime_for_field_type diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index eecb72628f..543cfb7f4f 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -53,6 +53,10 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { mut skip_state := ComptimeBranchSkipState.unknown mut found_branch := false // Whether a matching branch was found- skip the rest mut is_comptime_type_is_expr := false // if `$if T is string` + last_in_comptime_if := c.comptime.inside_comptime_if + defer { + c.comptime.inside_comptime_if = last_in_comptime_if + } for i in 0 .. node.branches.len { mut branch := node.branches[i] if branch.cond is ast.ParExpr && !c.pref.translated && !c.file.is_translated { diff --git a/vlib/v/checker/tests/comptime_selector_assign.out b/vlib/v/checker/tests/comptime_selector_assign.out new file mode 100644 index 0000000000..a17b7f28ca --- /dev/null +++ b/vlib/v/checker/tests/comptime_selector_assign.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/comptime_selector_assign.vv:18:24: error: mismatched types: check field type with $if to avoid this problem + 16 | typ.$(field.name) = 2 + 17 | } + 18 | typ.$(field.name) = 3 + | ^ + 19 | } + 20 | } diff --git a/vlib/v/checker/tests/comptime_selector_assign.vv b/vlib/v/checker/tests/comptime_selector_assign.vv new file mode 100644 index 0000000000..d3e759a653 --- /dev/null +++ b/vlib/v/checker/tests/comptime_selector_assign.vv @@ -0,0 +1,31 @@ +import x.json2 { Any, raw_decode } + +struct Income { +mut: + email string + code int +} + +pub fn structuring[T](res Any) T { + mut typ := T{} + res_map := res.as_map() + + $for field in T.fields { + if field.name in res_map { + $if field.typ is int { + typ.$(field.name) = 2 + } + typ.$(field.name) = 3 + } + } + return typ +} + +fn main() { + res := raw_decode('{ + "email": ["sdvsdv", "sds"], + "code": 12 + }')!.as_map() + + structuring[Income](res) +} diff --git a/vlib/v/type_resolver/type_resolver.v b/vlib/v/type_resolver/type_resolver.v index 01530ee95f..05a8f9b49c 100644 --- a/vlib/v/type_resolver/type_resolver.v +++ b/vlib/v/type_resolver/type_resolver.v @@ -15,6 +15,9 @@ pub mut: comptime_loop_id int // $for inside_comptime_for bool + // $if + inside_comptime_if bool + has_different_types bool // .variants comptime_for_variant_var string // .fields