Skip to content

Commit

Permalink
checker,cgen: fix generic map inferring key and value types (#20959)
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp committed Mar 4, 2024
1 parent 115f83d commit 6279e8a
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 2 deletions.
28 changes: 28 additions & 0 deletions vlib/v/checker/fn.v
Expand Up @@ -1572,6 +1572,20 @@ fn (mut c Checker) resolve_comptime_args(func ast.Fn, node_ ast.CallExpr, concre
if arg_sym.info is ast.Array && param_typ.has_flag(.generic)
&& c.table.final_sym(param_typ).kind == .array {
ctyp = arg_sym.info.elem_type
} else if arg_sym.info is ast.Map && param_typ.has_flag(.generic)
&& c.table.final_sym(param_typ).kind == .map {
map_info := c.table.final_sym(param_typ).info as ast.Map
key_is_generic := map_info.key_type.has_flag(.generic)
if key_is_generic {
ctyp = c.unwrap_generic(arg_sym.info.key_type)
}
if map_info.value_type.has_flag(.generic) {
if key_is_generic {
comptime_args[k] = ctyp
k++
}
ctyp = c.unwrap_generic(arg_sym.info.key_type)
}
}
comptime_args[k] = ctyp
}
Expand Down Expand Up @@ -1645,9 +1659,23 @@ fn (mut c Checker) resolve_comptime_args(func ast.Fn, node_ ast.CallExpr, concre
ct_value := c.comptime.get_comptime_var_type(call_arg.expr)
param_typ_sym := c.table.sym(param_typ)
if ct_value != ast.void_type {
arg_sym := c.table.final_sym(call_arg.typ)
cparam_type_sym := c.table.sym(c.unwrap_generic(ct_value))
if param_typ_sym.kind == .array && cparam_type_sym.info is ast.Array {
comptime_args[k] = cparam_type_sym.info.elem_type
} else if arg_sym.info is ast.Map && param_typ.has_flag(.generic)
&& c.table.final_sym(param_typ).kind == .map {
map_info := c.table.final_sym(param_typ).info as ast.Map
key_is_generic := map_info.key_type.has_flag(.generic)
if key_is_generic {
comptime_args[k] = c.unwrap_generic(arg_sym.info.key_type)
}
if map_info.value_type.has_flag(.generic) {
if key_is_generic {
k++
}
comptime_args[k] = c.unwrap_generic(arg_sym.info.key_type)
}
} else {
comptime_args[k] = ct_value
}
Expand Down
28 changes: 26 additions & 2 deletions vlib/v/gen/c/fn.v
Expand Up @@ -1202,6 +1202,18 @@ fn (mut g Gen) resolve_comptime_args(func ast.Fn, mut node_ ast.CallExpr, concre
if param_sym.info.key_type.nr_muls() > 0 && ctyp.nr_muls() > 0 {
ctyp = ctyp.set_nr_muls(0)
}
} else {
key_is_generic := param_sym.info.key_type.has_flag(.generic)
if key_is_generic {
ctyp = g.unwrap_generic(arg_sym.info.key_type)
}
if param_sym.info.value_type.has_flag(.generic) {
if key_is_generic {
comptime_args[k] = ctyp
k++
}
ctyp = g.unwrap_generic(arg_sym.info.value_type)
}
}
}
comptime_args[k] = ctyp
Expand Down Expand Up @@ -1274,10 +1286,22 @@ fn (mut g Gen) resolve_comptime_args(func ast.Fn, mut node_ ast.CallExpr, concre
} else if mut call_arg.expr is ast.ComptimeSelector {
comptime_args[k] = g.comptime.comptime_for_field_type
arg_sym := g.table.final_sym(call_arg.typ)
param_typ_sym := g.table.sym(param_typ)
param_sym := g.table.sym(param_typ)
if arg_sym.kind == .array && param_typ.has_flag(.generic)
&& param_typ_sym.kind == .array {
&& param_sym.kind == .array {
comptime_args[k] = g.get_generic_array_element_type(arg_sym.info as ast.Array)
} else if arg_sym.info is ast.Map && param_sym.info is ast.Map
&& param_typ.has_flag(.generic) {
key_is_generic := param_sym.info.key_type.has_flag(.generic)
if key_is_generic {
comptime_args[k] = g.unwrap_generic(arg_sym.info.key_type)
}
if param_sym.info.value_type.has_flag(.generic) {
if key_is_generic {
k++
}
comptime_args[k] = g.unwrap_generic(arg_sym.info.value_type)
}
}
if param_typ.nr_muls() > 0 && comptime_args[k].nr_muls() > 0 {
comptime_args[k] = comptime_args[k].set_nr_muls(0)
Expand Down
47 changes: 47 additions & 0 deletions vlib/v/tests/comptime_map_generic_test.v
@@ -0,0 +1,47 @@
import x.json2

struct User {
pub mut:
numbers map[string]string
}

fn test_main() {
user_json := '{"numbers":{"home":"123456","work":"987653"}}'
res := json2.raw_decode(user_json)!.as_map()

mut numbers := map[string]string{}
decode_map(mut numbers, res['numbers']!.as_map())!
assert numbers == {
'home': '123456'
'work': '987653'
}

assert decode_struct[User](res)! == User{
numbers: {
'home': '123456'
'work': '987653'
}
}
}

fn decode_struct[T](res map[string]json2.Any) !T {
mut typ := T{}
$for field in T.fields {
$if field.is_map {
decode_map(mut typ.$(field.name), res[field.name]!.as_map())!
} $else {
return error("The type of `${field.name}` can't be decoded.")
}
}
return typ
}

fn decode_map[V](mut m map[string]V, res map[string]json2.Any) ! {
$if V is $string {
for k, v in res {
m[k] = v.str()
}
} $else {
return error("The map value can't be decoded.")
}
}

0 comments on commit 6279e8a

Please sign in to comment.