diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 78c461d4d7ba65..24df5c239c2aae 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -8393,8 +8393,9 @@ fn (mut g Gen) lock_expr_mtx(expr ast.Expr) { } fn (mut g Gen) map_init(node ast.MapInit) { - unwrap_key_typ := g.unwrap_generic(node.key_type) - unwrap_val_typ := g.unwrap_generic(node.value_type).clear_flag(.result) + unwrap_key_typ, unwrap_val_typ_ := g.resolved_map_key_value_types(node.typ, node.key_type, + node.value_type) + unwrap_val_typ := unwrap_val_typ_.clear_flag(.result) key_typ_str := g.styp(unwrap_key_typ) value_typ_str := g.styp(unwrap_val_typ) value_sym := g.table.sym(unwrap_val_typ) @@ -8416,8 +8417,8 @@ fn (mut g Gen) map_init(node ast.MapInit) { styp = g.styp(node.typ) g.write('(${styp}*)builtin__memdup(ADDR(${styp}, ') } - noscan_key := g.check_noscan(node.key_type) - noscan_value := g.check_noscan(node.value_type) + noscan_key := g.check_noscan(unwrap_key_typ) + noscan_value := g.check_noscan(unwrap_val_typ) mut noscan := if noscan_key.len != 0 || noscan_value.len != 0 { '_noscan' } else { '' } if noscan.len != 0 { if noscan_key.len != 0 { diff --git a/vlib/v/gen/c/if.v b/vlib/v/gen/c/if.v index 403341c0f66596..6ae82bbb1db995 100644 --- a/vlib/v/gen/c/if.v +++ b/vlib/v/gen/c/if.v @@ -5,6 +5,25 @@ module c import v.ast +fn (mut g Gen) resolved_if_guard_expr_type(expr ast.Expr, default_type ast.Type) ast.Type { + if expr is ast.IndexExpr { + left_type := g.resolved_map_type_from_expr(expr.left, expr.left_type) + left_sym := g.table.final_sym(g.unwrap_generic(left_type)) + if left_sym.kind == .map { + map_info := left_sym.map_info() + _, mut value_type := g.resolved_map_key_value_types(left_type, map_info.key_type, + map_info.value_type) + if default_type.has_flag(.option) && !value_type.has_flag(.option) { + value_type = value_type.set_flag(.option) + } else if default_type.has_flag(.result) && !value_type.has_flag(.result) { + value_type = value_type.set_flag(.result) + } + return value_type + } + } + return g.unwrap_generic(g.recheck_concrete_type(default_type)) +} + fn (mut g Gen) if_guard_var_needs_gc_pin(scope &ast.Scope, name string) bool { if g.pref.gc_mode !in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt] { return false @@ -509,7 +528,7 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { } } } else { - resolved := g.unwrap_generic(g.recheck_concrete_type(guard_expr_type)) + resolved := g.resolved_if_guard_expr_type(branch.cond.expr, guard_expr_type) if resolved != ast.void_type { guard_expr_type = resolved } diff --git a/vlib/v/gen/c/index.v b/vlib/v/gen/c/index.v index 7d9ea80e2854b3..b8e4a092d11548 100644 --- a/vlib/v/gen/c/index.v +++ b/vlib/v/gen/c/index.v @@ -689,14 +689,7 @@ fn (mut g Gen) index_of_fixed_array(node ast.IndexExpr, sym ast.TypeSymbol) { fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) { gen_or := node.or_expr.kind != .absent || node.is_option - mut map_left_type := g.recheck_concrete_type(node.left_type) - resolved_left_type := g.recheck_concrete_type(g.resolved_expr_type(node.left, node.left_type)) - if resolved_left_type != 0 - && (g.cur_concrete_types.len > 0 || map_left_type == 0 || map_left_type.has_flag(.generic) - || g.type_has_unresolved_generic_parts(map_left_type) - || g.unwrap_generic(resolved_left_type) != g.unwrap_generic(map_left_type)) { - map_left_type = resolved_left_type - } + map_left_type := g.resolved_map_type_from_expr(node.left, node.left_type) mut left_is_ptr := map_left_type.is_ptr() if !left_is_ptr && g.is_assign_lhs && node.left is ast.Ident && g.resolved_ident_is_auto_heap(node.left) { @@ -712,14 +705,10 @@ fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) { } else { sym.info as ast.Map } - mut key_type := g.unwrap_generic(g.recheck_concrete_type(info.key_type)) - mut val_type := g.unwrap_generic(g.recheck_concrete_type(info.value_type)) - if key_type == 0 { - key_type = info.key_type - } - if val_type == 0 { - val_type = info.value_type - } + key_type_, val_type_ := g.resolved_map_key_value_types(map_left_type, info.key_type, + info.value_type) + mut key_type := key_type_ + mut val_type := val_type_ if node.left is ast.Ident { ident_key_type := g.resolved_ident_map_key_type(node.left) if ident_key_type != 0 { diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index 8f67cd699835e6..fa5b972b555ffc 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -1087,7 +1087,18 @@ fn (mut g Gen) struct_init_field_value(sfield ast.StructInitField) { expected_unwrap_typ := g.unwrap_generic(sfield.expected_type) expected_unwrap_sym := g.table.final_sym(expected_unwrap_typ) is_auto_deref_var := sfield.expr.is_auto_deref_var() - if expected_unwrap_sym.info is ast.ArrayFixed && sfield.expr is ast.ArrayInit + if expected_unwrap_sym.kind == .map && sfield.expr is ast.MapInit + && !sfield.expected_type.has_option_or_result() + && !sfield.expected_type.has_flag(.shared_f) + && !sfield.expected_type.has_flag(.atomic_f) { + expected_map_info := expected_unwrap_sym.map_info() + g.map_init(ast.MapInit{ + ...sfield.expr + typ: expected_unwrap_typ + key_type: expected_map_info.key_type + value_type: expected_map_info.value_type + }) + } else if expected_unwrap_sym.info is ast.ArrayFixed && sfield.expr is ast.ArrayInit && !sfield.expr.is_fixed && !sfield.expr.has_len && !sfield.expr.has_init && sfield.expr.exprs.len > 0 && !sfield.expected_type.has_flag(.option) { fixed_array_expr := ast.ArrayInit{ diff --git a/vlib/v/gen/c/utils.v b/vlib/v/gen/c/utils.v index bc1bb3d3cd968a..5f0e10bdc652e5 100644 --- a/vlib/v/gen/c/utils.v +++ b/vlib/v/gen/c/utils.v @@ -924,6 +924,52 @@ fn (mut g Gen) resolved_ident_map_value_type(expr ast.Ident) ast.Type { return 0 } +fn (mut g Gen) resolved_map_key_value_types(map_type ast.Type, fallback_key_type ast.Type, fallback_value_type ast.Type) (ast.Type, ast.Type) { + mut key_type := g.unwrap_generic(g.recheck_concrete_type(fallback_key_type)) + mut value_type := g.unwrap_generic(g.recheck_concrete_type(fallback_value_type)) + resolved_map_type := g.unwrap_generic(g.recheck_concrete_type(map_type)) + map_sym := g.table.final_sym(resolved_map_type) + if map_sym.kind == .map { + map_info := map_sym.map_info() + resolved_key := g.unwrap_generic(g.recheck_concrete_type(map_info.key_type)) + resolved_value := g.unwrap_generic(g.recheck_concrete_type(map_info.value_type)) + if resolved_key != 0 { + key_type = resolved_key + } + if resolved_value != 0 { + value_type = resolved_value + } + if key_type == ast.usize_type || value_type == ast.usize_type { + name_key_type, name_value_type := g.resolved_map_types_from_name(map_sym.name) + if key_type == ast.usize_type && name_key_type != 0 { + key_type = name_key_type + } + if value_type == ast.usize_type && name_value_type != 0 { + value_type = name_value_type + } + } + } + if key_type == 0 { + key_type = fallback_key_type + } + if value_type == 0 { + value_type = fallback_value_type + } + return key_type, value_type +} + +fn (mut g Gen) resolved_map_type_from_expr(expr ast.Expr, default_type ast.Type) ast.Type { + mut map_type := g.recheck_concrete_type(default_type) + resolved_type := g.recheck_concrete_type(g.resolved_expr_type(expr, default_type)) + if resolved_type != 0 + && (g.cur_concrete_types.len > 0 || map_type == 0 || map_type.has_flag(.generic) + || g.type_has_unresolved_generic_parts(map_type) + || g.unwrap_generic(resolved_type) != g.unwrap_generic(map_type)) { + map_type = resolved_type + } + return map_type +} + fn (mut g Gen) resolved_array_elem_type_from_name(name string) ast.Type { if !name.starts_with('[]') { return 0 diff --git a/vlib/v/generics/new_generics_regression_test.v b/vlib/v/generics/new_generics_regression_test.v index d7e47798a17bfc..3ed5d896ded380 100644 --- a/vlib/v/generics/new_generics_regression_test.v +++ b/vlib/v/generics/new_generics_regression_test.v @@ -117,9 +117,9 @@ fn run_new_generic_solver_tests(root_label string, test_cmd string, expected_sum println('') } -const expected_summsvc_generics = 'Summary for all V _test.v files: 114 failed, 180 passed, 294 total.' +const expected_summsvc_generics = 'Summary for all V _test.v files: 115 failed, 180 passed, 295 total.' // The exact failure count varies slightly across compilers. -const expected_summary_generics = 'Summary for all V _test.v files: 110 failed, 183 passed, 293 total.' +const expected_summary_generics = 'Summary for all V _test.v files: 111 failed, 184 passed, 295 total.' const expected_summsvc_vec = 'Summary for all V _test.v files: 3 failed, 3 total.' const expected_summary_vec = 'Summary for all V _test.v files: 3 failed, 3 total.' const expected_summsvc_flag = 'Summary for all V _test.v files: 21 passed, 21 total.' @@ -155,6 +155,7 @@ const failing_tests = [ 'vlib/v/tests/generics/generic_lambda_expr_test.v', 'vlib/v/tests/generics/generic_linked_list_ref_push_test.v', 'vlib/v/tests/generics/generic_map_alias_test.v', + 'vlib/v/tests/generics/generic_map_value_in_generic_struct_method_test.v', 'vlib/v/tests/generics/generic_match_expr_test.v', 'vlib/v/tests/generics/generic_match_generic_interface_type_test.v', 'vlib/v/tests/generics/generic_method_fn_field_result_recheck_test.v', diff --git a/vlib/v/tests/generics/generic_map_value_in_generic_struct_method_test.v b/vlib/v/tests/generics/generic_map_value_in_generic_struct_method_test.v new file mode 100644 index 00000000000000..0d6d56e57af6e2 --- /dev/null +++ b/vlib/v/tests/generics/generic_map_value_in_generic_struct_method_test.v @@ -0,0 +1,215 @@ +struct Wrapped[T] { + value T +} + +struct WrappedStore[T] { +mut: + data map[string]Wrapped[T] +} + +fn new_wrapped_store[T]() WrappedStore[T] { + return WrappedStore[T]{ + data: map[string]Wrapped[T]{} + } +} + +fn new_seeded_wrapped_store[T](key string, val T) WrappedStore[T] { + return WrappedStore[T]{ + data: { + key: Wrapped[T]{ + value: val + } + } + } +} + +fn (mut store WrappedStore[T]) set(key string, val T) { + store.data[key] = Wrapped[T]{ + value: val + } +} + +fn (mut store WrappedStore[T]) get(key string) !T { + if record := store.data[key] { + return record.value + } + return error('missing') +} + +struct DirectStore[T] { +mut: + data map[string]T +} + +fn new_direct_store[T]() DirectStore[T] { + return DirectStore[T]{ + data: map[string]T{} + } +} + +fn new_seeded_direct_store[T](key string, val T) DirectStore[T] { + return DirectStore[T]{ + data: { + key: val + } + } +} + +fn (mut store DirectStore[T]) set(key string, val T) { + store.data[key] = val +} + +fn (mut store DirectStore[T]) get(key string) !T { + if record := store.data[key] { + return record + } + return error('missing') +} + +struct IntKeyWrappedStore[T] { +mut: + data map[int]Wrapped[T] +} + +fn new_int_key_wrapped_store[T]() IntKeyWrappedStore[T] { + return IntKeyWrappedStore[T]{ + data: map[int]Wrapped[T]{} + } +} + +fn new_seeded_int_key_wrapped_store[T](key int, val T) IntKeyWrappedStore[T] { + return IntKeyWrappedStore[T]{ + data: { + key: Wrapped[T]{ + value: val + } + } + } +} + +fn (mut store IntKeyWrappedStore[T]) set(key int, val T) { + store.data[key] = Wrapped[T]{ + value: val + } +} + +fn (mut store IntKeyWrappedStore[T]) get(key int) !T { + if record := store.data[key] { + return record.value + } + return error('missing') +} + +struct IntKeyDirectStore[T] { +mut: + data map[int]T +} + +fn new_int_key_direct_store[T]() IntKeyDirectStore[T] { + return IntKeyDirectStore[T]{ + data: map[int]T{} + } +} + +fn new_seeded_int_key_direct_store[T](key int, val T) IntKeyDirectStore[T] { + return IntKeyDirectStore[T]{ + data: { + key: val + } + } +} + +fn (mut store IntKeyDirectStore[T]) set(key int, val T) { + store.data[key] = val +} + +fn (mut store IntKeyDirectStore[T]) get(key int) !T { + if record := store.data[key] { + return record + } + return error('missing') +} + +struct UserRecord { + name string +} + +fn test_generic_struct_method_with_generic_map_value() { + mut string_store := new_wrapped_store[string]() + string_store.set('alpha', 'one') + assert string_store.get('alpha')! == 'one' + + mut record_store := new_wrapped_store[UserRecord]() + record_store.set('alpha', UserRecord{ + name: 'two' + }) + assert record_store.get('alpha')!.name == 'two' + + mut seeded_string_store := new_seeded_wrapped_store('seed', 'three') + assert seeded_string_store.get('seed')! == 'three' + + mut seeded_record_store := new_seeded_wrapped_store('seed', UserRecord{ + name: 'four' + }) + assert seeded_record_store.get('seed')!.name == 'four' +} + +fn test_generic_struct_method_with_int_key_generic_map_value() { + mut string_store := new_int_key_wrapped_store[string]() + string_store.set(1, 'one') + assert string_store.get(1)! == 'one' + + mut record_store := new_int_key_wrapped_store[UserRecord]() + record_store.set(1, UserRecord{ + name: 'two' + }) + assert record_store.get(1)!.name == 'two' + + mut seeded_string_store := new_seeded_int_key_wrapped_store(2, 'three') + assert seeded_string_store.get(2)! == 'three' + + mut seeded_record_store := new_seeded_int_key_wrapped_store(2, UserRecord{ + name: 'four' + }) + assert seeded_record_store.get(2)!.name == 'four' +} + +fn test_generic_struct_method_with_int_key_direct_generic_map_value() { + mut string_store := new_int_key_direct_store[string]() + string_store.set(1, 'one') + assert string_store.get(1)! == 'one' + + mut record_store := new_int_key_direct_store[UserRecord]() + record_store.set(1, UserRecord{ + name: 'two' + }) + assert record_store.get(1)!.name == 'two' + + mut seeded_string_store := new_seeded_int_key_direct_store(2, 'three') + assert seeded_string_store.get(2)! == 'three' + + mut seeded_record_store := new_seeded_int_key_direct_store(2, UserRecord{ + name: 'four' + }) + assert seeded_record_store.get(2)!.name == 'four' +} + +fn test_generic_struct_method_with_direct_generic_map_value() { + mut string_store := new_direct_store[string]() + string_store.set('alpha', 'one') + assert string_store.get('alpha')! == 'one' + + mut record_store := new_direct_store[UserRecord]() + record_store.set('alpha', UserRecord{ + name: 'two' + }) + assert record_store.get('alpha')!.name == 'two' + + mut seeded_string_store := new_seeded_direct_store('seed', 'three') + assert seeded_string_store.get('seed')! == 'three' + + mut seeded_record_store := new_seeded_direct_store('seed', UserRecord{ + name: 'four' + }) + assert seeded_record_store.get('seed')!.name == 'four' +} diff --git a/vlib/v/tests/options/option_map_struct_field_init_test.v b/vlib/v/tests/options/option_map_struct_field_init_test.v new file mode 100644 index 00000000000000..9c3ac4d0f53651 --- /dev/null +++ b/vlib/v/tests/options/option_map_struct_field_init_test.v @@ -0,0 +1,14 @@ +struct Config { + values ?map[string]int +} + +fn test_option_map_struct_field_init_with_literal() { + cfg := Config{ + values: { + 'one': 1 + 'two': 2 + } + } + assert cfg.values?['one']! == 1 + assert cfg.values?['two']! == 2 +}