diff --git a/doc/docs.md b/doc/docs.md
index 99aa3bbbcb6959..9992711193f812 100644
--- a/doc/docs.md
+++ b/doc/docs.md
@@ -78,6 +78,7 @@ by using any of the following commands in a terminal:
* [Array slices](#array-slices)
* [Fixed size arrays](#fixed-size-arrays)
* [Maps](#maps)
+ * [Map update syntax](#map-update-syntax)
@@ -480,7 +481,7 @@ fn main() {
}
}
```
-While variable shadowing is not allowed, field shadowing is allowed.
+While variable shadowing is not allowed, field shadowing is allowed.
```v
pub struct Dimension {
width int = -1
@@ -1462,6 +1463,36 @@ See all methods of
and
[maps](https://modules.vlang.io/maps.html).
+### Map update syntax
+
+As with stucts, V lets you initialise a map with an update applied on top of
+another map:
+
+```v
+const base_map = {
+ 'a': 4
+ 'b': 5
+}
+
+foo := {
+ ...base_map
+ 'b': 88
+ 'c': 99
+}
+
+println(foo) // {'a': 4, 'b': 88, 'c': 99}
+```
+
+This is functionally equivalent to cloning the map and updating it, except that
+you don't have to declare a mutable variable:
+
+```v failcompile
+// same as above (except mutable)
+mut foo := base_map.clone()
+foo['b'] = 88
+foo['c'] = 99
+```
+
## Module imports
For information about creating a module, see [Modules](#modules).
@@ -5634,7 +5665,7 @@ serializers for any data format. V has compile time `if` and `for` constructs:
#### .fields
-You can iterate over struct fields using `.fields`, it also works with generic types
+You can iterate over struct fields using `.fields`, it also works with generic types
(e.g. `T.fields`) and generic arguments (e.g. `param.fields` where `fn gen[T](param T) {`).
```v
@@ -7325,4 +7356,4 @@ Assignment Operators
+= -= *= /= %=
&= |= ^=
>>= <<= >>>=
-```
\ No newline at end of file
+```
diff --git a/vlib/builtin/map.v b/vlib/builtin/map.v
index d5daae6280ffa3..5fb8c4bcf2a028 100644
--- a/vlib/builtin/map.v
+++ b/vlib/builtin/map.v
@@ -284,6 +284,20 @@ fn new_map_init(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_
return out
}
+fn new_map_update_init(update &map, n int, key_bytes int, value_bytes int, keys voidptr, values voidptr) map {
+ mut out := unsafe { update.clone() }
+ mut pkey := &u8(keys)
+ mut pval := &u8(values)
+ for _ in 0 .. n {
+ unsafe {
+ out.set(pkey, pval)
+ pkey = pkey + key_bytes
+ pval = pval + value_bytes
+ }
+ }
+ return out
+}
+
// move moves the map to a new location in memory.
// It does this by copying to a new location, then setting the
// old location to all `0` with `vmemset`
diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v
index e744e72762729a..a9b80b84fe75ae 100644
--- a/vlib/v/ast/ast.v
+++ b/vlib/v/ast/ast.v
@@ -1502,12 +1502,16 @@ pub:
comments [][]Comment // comments after key-value pairs
pre_cmnts []Comment // comments before the first key-value pair
pub mut:
- keys []Expr
- vals []Expr
- val_types []Type
- typ Type
- key_type Type
- value_type Type
+ keys []Expr
+ vals []Expr
+ val_types []Type
+ typ Type
+ key_type Type
+ value_type Type
+ has_update_expr bool // has `...a`
+ update_expr Expr // `a` in `...a`
+ update_expr_pos token.Pos
+ update_expr_comments []Comment
}
// s[10..20]
diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v
index f3aa960a2360d0..dc106ca22cbe6c 100644
--- a/vlib/v/checker/containers.v
+++ b/vlib/v/checker/containers.v
@@ -407,7 +407,7 @@ fn (mut c Checker) array_fixed_has_unresolved_size(info &ast.ArrayFixed) bool {
fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type {
// `map = {}`
- if node.keys.len == 0 && node.vals.len == 0 && node.typ == 0 {
+ if node.keys.len == 0 && node.vals.len == 0 && !node.has_update_expr && node.typ == 0 {
sym := c.table.sym(c.expected_type)
if sym.kind == .map {
info := sym.map_info()
@@ -455,90 +455,107 @@ fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type {
return node.typ
}
- if node.keys.len > 0 && node.vals.len > 0 {
- mut key0_type := ast.void_type
- mut val0_type := ast.void_type
+ if (node.keys.len > 0 && node.vals.len > 0) || node.has_update_expr {
+ mut map_type := ast.void_type
use_expected_type := c.expected_type != ast.void_type && !c.inside_const
&& c.table.sym(c.expected_type).kind == .map && !(c.inside_fn_arg
&& c.expected_type.has_flag(.generic))
if use_expected_type {
- sym := c.table.sym(c.expected_type)
+ map_type = c.expected_type
+ }
+ if node.has_update_expr {
+ update_type := c.expr(mut node.update_expr)
+ if map_type != ast.void_type {
+ if update_type != map_type {
+ msg := c.expected_msg(update_type, map_type)
+ c.error('invalid map update: ${msg}', node.update_expr_pos)
+ }
+ } else if c.table.sym(update_type).kind != .map {
+ c.error('invalid map update: non-map type', node.update_expr_pos)
+ } else {
+ map_type = update_type
+ }
+ }
+
+ mut map_key_type := ast.void_type
+ mut map_val_type := ast.void_type
+ if map_type != ast.void_type {
+ sym := c.table.sym(map_type)
info := sym.map_info()
- key0_type = c.unwrap_generic(info.key_type)
- val0_type = c.unwrap_generic(info.value_type)
- } else {
+ map_key_type = info.key_type
+ map_val_type = info.value_type
+ } else if node.keys.len > 0 {
// `{'age': 20}`
mut key_ := node.keys[0]
- key0_type = ast.mktyp(c.expr(mut key_))
+ map_key_type = ast.mktyp(c.expr(mut key_))
if node.keys[0].is_auto_deref_var() {
- key0_type = key0_type.deref()
+ map_key_type = map_key_type.deref()
}
mut val_ := node.vals[0]
- val0_type = ast.mktyp(c.expr(mut val_))
+ map_val_type = ast.mktyp(c.expr(mut val_))
if node.vals[0].is_auto_deref_var() {
- val0_type = val0_type.deref()
+ map_val_type = map_val_type.deref()
+ }
+ node.val_types << map_val_type
+ if node.keys.len == 1 && map_val_type == ast.none_type {
+ c.error('map value cannot be only `none`', node.vals[0].pos())
}
- node.val_types << val0_type
}
- key0_type = c.unwrap_generic(key0_type)
- val0_type = c.unwrap_generic(val0_type)
- map_type := ast.new_type(c.table.find_or_register_map(key0_type, val0_type))
- node.typ = map_type
- node.key_type = key0_type
- node.value_type = val0_type
- map_value_sym := c.table.sym(node.value_type)
- expecting_interface_map := map_value_sym.kind == .interface_
- //
- mut same_key_type := true
+ map_key_type = c.unwrap_generic(map_key_type)
+ map_val_type = c.unwrap_generic(map_val_type)
- if node.keys.len == 1 && val0_type == ast.none_type {
- c.error('map value cannot be only `none`', node.vals[0].pos())
- }
+ node.typ = ast.new_type(c.table.find_or_register_map(map_key_type, map_val_type))
+ node.key_type = map_key_type
+ node.value_type = map_val_type
+ map_value_sym := c.table.sym(map_val_type)
+ expecting_interface_map := map_value_sym.kind == .interface_
+ mut same_key_type := true
for i, mut key in node.keys {
- if i == 0 && !use_expected_type {
- continue
+ if i == 0 && map_type == ast.void_type {
+ continue // skip first key/value if we processed them above
}
mut val := node.vals[i]
- c.expected_type = key0_type
+ c.expected_type = map_key_type
key_type := c.expr(mut key)
- c.expected_type = val0_type
+ c.expected_type = map_val_type
val_type := c.expr(mut val)
node.val_types << val_type
val_type_sym := c.table.sym(val_type)
- if !c.check_types(key_type, key0_type) || (i == 0 && key_type.is_number()
- && key0_type.is_number() && key0_type != ast.mktyp(key_type)) {
- msg := c.expected_msg(key_type, key0_type)
+ if !c.check_types(key_type, map_key_type)
+ || (i == 0 && key_type.is_number() && map_key_type.is_number()
+ && map_key_type != ast.mktyp(key_type)) {
+ msg := c.expected_msg(key_type, map_key_type)
c.error('invalid map key: ${msg}', key.pos())
same_key_type = false
}
if expecting_interface_map {
- if val_type == node.value_type {
+ if val_type == map_val_type {
continue
}
if val_type_sym.kind == .struct_
- && c.type_implements(val_type, node.value_type, val.pos()) {
+ && c.type_implements(val_type, map_val_type, val.pos()) {
node.vals[i] = ast.CastExpr{
expr: val
- typname: c.table.get_type_name(node.value_type)
- typ: node.value_type
+ typname: c.table.get_type_name(map_val_type)
+ typ: map_val_type
expr_type: val_type
pos: val.pos()
}
continue
} else {
- msg := c.expected_msg(val_type, node.value_type)
+ msg := c.expected_msg(val_type, map_val_type)
c.error('invalid map value: ${msg}', val.pos())
}
}
- if val_type == ast.none_type && val0_type.has_flag(.option) {
+ if val_type == ast.none_type && map_val_type.has_flag(.option) {
continue
}
- if !c.check_types(val_type, val0_type)
- || val0_type.has_flag(.option) != val_type.has_flag(.option)
- || (i == 0 && val_type.is_number() && val0_type.is_number()
- && val0_type != ast.mktyp(val_type)) {
- msg := c.expected_msg(val_type, val0_type)
+ if !c.check_types(val_type, map_val_type)
+ || map_val_type.has_flag(.option) != val_type.has_flag(.option)
+ || (i == 0 && val_type.is_number() && map_val_type.is_number()
+ && map_val_type != ast.mktyp(val_type)) {
+ msg := c.expected_msg(val_type, map_val_type)
c.error('invalid map value: ${msg}', val.pos())
}
}
@@ -547,7 +564,6 @@ fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type {
c.check_dup_keys(node, i)
}
}
- return map_type
}
return node.typ
}
diff --git a/vlib/v/checker/tests/map_init_invalid_update.out b/vlib/v/checker/tests/map_init_invalid_update.out
new file mode 100644
index 00000000000000..08d20b6f3d06dc
--- /dev/null
+++ b/vlib/v/checker/tests/map_init_invalid_update.out
@@ -0,0 +1,14 @@
+vlib/v/checker/tests/map_init_invalid_update.vv:8:6: error: invalid map update: non-map type
+ 6 | foo := 'foo is a string'
+ 7 | a := {
+ 8 | ...foo // not a map
+ | ~~~
+ 9 | 'a': 5
+ 10 | 'b': 6
+vlib/v/checker/tests/map_init_invalid_update.vv:19:6: error: invalid map update: non-map type
+ 17 | println(b)
+ 18 | c := {
+ 19 | ...Foo{9} // also not ok
+ | ~~~~~~
+ 20 | }
+ 21 | println(c)
diff --git a/vlib/v/checker/tests/map_init_invalid_update.vv b/vlib/v/checker/tests/map_init_invalid_update.vv
new file mode 100644
index 00000000000000..8bf148fc84d8fd
--- /dev/null
+++ b/vlib/v/checker/tests/map_init_invalid_update.vv
@@ -0,0 +1,22 @@
+struct Foo {
+ x int
+}
+
+fn main() {
+ foo := 'foo is a string'
+ a := {
+ ...foo // not a map
+ 'a': 5
+ 'b': 6
+ }
+ println(a)
+ b := {
+ ...(a.clone()) // ok
+ 'c': 99
+ }
+ println(b)
+ c := {
+ ...Foo{9} // also not ok
+ }
+ println(c)
+}
diff --git a/vlib/v/checker/tests/map_init_wrong_expected_type.out b/vlib/v/checker/tests/map_init_wrong_expected_type.out
new file mode 100644
index 00000000000000..1ad16d8431cb55
--- /dev/null
+++ b/vlib/v/checker/tests/map_init_wrong_expected_type.out
@@ -0,0 +1,49 @@
+vlib/v/checker/tests/map_init_wrong_expected_type.vv:18:9: error: invalid map value: expected `int`, not `string`
+ 16 | b := Bar{
+ 17 | m: {
+ 18 | 'a': '5'
+ | ~~~
+ 19 | 'b': '6'
+ 20 | } // bad
+vlib/v/checker/tests/map_init_wrong_expected_type.vv:19:9: error: invalid map value: expected `int`, not `string`
+ 17 | m: {
+ 18 | 'a': '5'
+ 19 | 'b': '6'
+ | ~~~
+ 20 | } // bad
+ 21 | }
+vlib/v/checker/tests/map_init_wrong_expected_type.vv:36:9: error: invalid map value: expected `int`, not `string`
+ 34 | m: {
+ 35 | ...a.m
+ 36 | 'c': '7'
+ | ~~~
+ 37 | } // bad values
+ 38 | }
+vlib/v/checker/tests/map_init_wrong_expected_type.vv:43:9: error: invalid map update: expected `map[string]int`, not `map[string]string`
+ 41 | f := Bar{
+ 42 | m: {
+ 43 | ...x.m
+ | ^
+ 44 | } // bad update
+ 45 | }
+vlib/v/checker/tests/map_init_wrong_expected_type.vv:48:9: error: invalid map update: expected `map[string]int`, not `map[string]string`
+ 46 | g := Bar{
+ 47 | m: {
+ 48 | ...x.m
+ | ^
+ 49 | 'c': 7
+ 50 | } // bad update, ok values
+vlib/v/checker/tests/map_init_wrong_expected_type.vv:54:9: error: invalid map update: expected `map[string]int`, not `map[string]string`
+ 52 | h := Bar{
+ 53 | m: {
+ 54 | ...x.m
+ | ^
+ 55 | 'c': '7'
+ 56 | } // bad update, bad values
+vlib/v/checker/tests/map_init_wrong_expected_type.vv:55:9: error: invalid map value: expected `int`, not `string`
+ 53 | m: {
+ 54 | ...x.m
+ 55 | 'c': '7'
+ | ~~~
+ 56 | } // bad update, bad values
+ 57 | }
diff --git a/vlib/v/checker/tests/map_init_wrong_expected_type.vv b/vlib/v/checker/tests/map_init_wrong_expected_type.vv
new file mode 100644
index 00000000000000..ee6b0a06df72a8
--- /dev/null
+++ b/vlib/v/checker/tests/map_init_wrong_expected_type.vv
@@ -0,0 +1,66 @@
+struct Foo {
+ m map[string]string
+}
+
+struct Bar {
+ m map[string]int
+}
+
+fn main() {
+ a := Bar{
+ m: {
+ 'a': 5
+ 'b': 6
+ } // ok
+ }
+ b := Bar{
+ m: {
+ 'a': '5'
+ 'b': '6'
+ } // bad
+ }
+ c := Bar{
+ m: {
+ ...a.m
+ } // ok
+ }
+ d := Bar{
+ m: {
+ ...a.m
+ 'c': 7
+ } // ok
+ }
+ e := Bar{
+ m: {
+ ...a.m
+ 'c': '7'
+ } // bad values
+ }
+
+ x := Foo{}
+ f := Bar{
+ m: {
+ ...x.m
+ } // bad update
+ }
+ g := Bar{
+ m: {
+ ...x.m
+ 'c': 7
+ } // bad update, ok values
+ }
+ h := Bar{
+ m: {
+ ...x.m
+ 'c': '7'
+ } // bad update, bad values
+ }
+ println(a)
+ println(b)
+ println(c)
+ println(d)
+ println(e)
+ println(f)
+ println(g)
+ println(h)
+}
diff --git a/vlib/v/checker/tests/map_init_wrong_update_type.out b/vlib/v/checker/tests/map_init_wrong_update_type.out
new file mode 100644
index 00000000000000..57197267e6fd04
--- /dev/null
+++ b/vlib/v/checker/tests/map_init_wrong_update_type.out
@@ -0,0 +1,28 @@
+vlib/v/checker/tests/map_init_wrong_update_type.vv:12:8: error: invalid map value: expected `StrB`, not `int literal`
+ 10 | a := {
+ 11 | ...foo // not ok
+ 12 | 'a': 5
+ | ^
+ 13 | 'b': 6
+ 14 | }
+vlib/v/checker/tests/map_init_wrong_update_type.vv:13:8: error: invalid map value: expected `StrB`, not `int literal`
+ 11 | ...foo // not ok
+ 12 | 'a': 5
+ 13 | 'b': 6
+ | ^
+ 14 | }
+ 15 | b := {
+vlib/v/checker/tests/map_init_wrong_update_type.vv:17:3: error: invalid map key: expected `StrA`, not `Bar`
+ 15 | b := {
+ 16 | ...foo // not ok
+ 17 | Bar{'yes'}: '5'
+ | ~~~~~~~~~~
+ 18 | Bar{'now'}: '6'
+ 19 | }
+vlib/v/checker/tests/map_init_wrong_update_type.vv:18:3: error: invalid map key: expected `StrA`, not `Bar`
+ 16 | ...foo // not ok
+ 17 | Bar{'yes'}: '5'
+ 18 | Bar{'now'}: '6'
+ | ~~~~~~~~~~
+ 19 | }
+ 20 | c := {
diff --git a/vlib/v/checker/tests/map_init_wrong_update_type.vv b/vlib/v/checker/tests/map_init_wrong_update_type.vv
new file mode 100644
index 00000000000000..618028b7670964
--- /dev/null
+++ b/vlib/v/checker/tests/map_init_wrong_update_type.vv
@@ -0,0 +1,28 @@
+struct Bar {
+ x string
+}
+
+type StrA = string
+type StrB = string
+
+fn main() {
+ foo := map[StrA]StrB{}
+ a := {
+ ...foo // not ok
+ 'a': 5
+ 'b': 6
+ }
+ b := {
+ ...foo // not ok
+ Bar{'yes'}: '5'
+ Bar{'now'}: '6'
+ }
+ c := {
+ ...foo // ok
+ 'up': 'down'
+ 'left': 'right'
+ }
+ println(a)
+ println(b)
+ println(c)
+}
diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v
index e37baad3098eff..2f450b329c2703 100644
--- a/vlib/v/fmt/fmt.v
+++ b/vlib/v/fmt/fmt.v
@@ -2624,7 +2624,7 @@ pub fn (mut f Fmt) lock_expr(node ast.LockExpr) {
}
pub fn (mut f Fmt) map_init(node ast.MapInit) {
- if node.keys.len == 0 {
+ if node.keys.len == 0 && !node.has_update_expr {
if node.typ > ast.void_type {
sym := f.table.sym(node.typ)
info := sym.info as ast.Map
@@ -2644,6 +2644,15 @@ pub fn (mut f Fmt) map_init(node ast.MapInit) {
f.writeln('{')
f.indent++
f.comments(node.pre_cmnts)
+ if node.has_update_expr {
+ f.write('...')
+ f.expr(node.update_expr)
+ f.comments(node.update_expr_comments,
+ prev_line: node.update_expr_pos.last_line
+ has_nl: false
+ )
+ f.writeln('')
+ }
mut max_field_len := 0
mut skeys := []string{}
for key in node.keys {
diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v
index 6a238a478ebce7..2e6a8888aad0e3 100644
--- a/vlib/v/gen/c/cgen.v
+++ b/vlib/v/gen/c/cgen.v
@@ -4136,25 +4136,25 @@ fn (mut g Gen) map_init(node ast.MapInit) {
}
}
if size > 0 {
- if value_sym.kind == .function {
- g.writeln('new_map_init${noscan}(${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn}, ${size}, sizeof(${key_typ_str}), sizeof(voidptr),')
+ effective_typ_str := if value_sym.kind == .function { 'voidptr' } else { value_typ_str }
+ if node.has_update_expr {
+ g.writeln('new_map_update_init(')
+ g.write('\t&(')
+ g.expr(node.update_expr)
+ g.writeln('), ${size}, sizeof(${key_typ_str}), sizeof(${effective_typ_str}),')
} else {
- g.writeln('new_map_init${noscan}(${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn}, ${size}, sizeof(${key_typ_str}), sizeof(${value_typ_str}),')
+ g.writeln('new_map_init${noscan}(${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn}, ${size}, sizeof(${key_typ_str}), sizeof(${effective_typ_str}),')
}
- g.writeln('\t\t_MOV((${key_typ_str}[${size}]){')
+ g.writeln('\t_MOV((${key_typ_str}[${size}]){')
for expr in node.keys {
- g.write('\t\t\t')
+ g.write('\t\t')
g.expr(expr)
- g.writeln(', ')
- }
- g.writeln('\t\t}),')
- if value_sym.kind == .function {
- g.writeln('\t\t_MOV((voidptr[${size}]){')
- } else {
- g.writeln('\t\t_MOV((${value_typ_str}[${size}]){')
+ g.writeln(',')
}
+ g.writeln('\t}),')
+ g.writeln('\t_MOV((${effective_typ_str}[${size}]){')
for i, expr in node.vals {
- g.write('\t\t\t')
+ g.write('\t\t')
if expr.is_auto_deref_var() {
g.write('*')
}
@@ -4167,12 +4167,15 @@ fn (mut g Gen) map_init(node ast.MapInit) {
}
g.writeln(', ')
}
- g.writeln('\t\t})')
- g.writeln('\t)')
+ g.writeln('\t})')
+ g.writeln(')')
+ } else if node.has_update_expr {
+ g.write('map_clone(&(')
+ g.expr(node.update_expr)
+ g.writeln('))')
} else {
- g.write('new_map${noscan}(sizeof(${key_typ_str}), sizeof(${value_typ_str}), ${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn})')
+ g.writeln('new_map${noscan}(sizeof(${key_typ_str}), sizeof(${value_typ_str}), ${hash_fn}, ${key_eq_fn}, ${clone_fn}, ${free_fn})')
}
- g.writeln('')
if g.is_shared {
g.write('}, sizeof(${shared_styp}))')
} else if is_amp {
diff --git a/vlib/v/parser/containers.v b/vlib/v/parser/containers.v
index 8d6a4f7be5f008..69ac305e029a90 100644
--- a/vlib/v/parser/containers.v
+++ b/vlib/v/parser/containers.v
@@ -196,7 +196,22 @@ fn (mut p Parser) map_init() ast.MapInit {
mut keys := []ast.Expr{}
mut vals := []ast.Expr{}
mut comments := [][]ast.Comment{}
+ mut has_update_expr := false
+ mut update_expr := ast.empty_expr
+ mut update_expr_comments := []ast.Comment{}
+ mut update_expr_pos := token.Pos{}
pre_cmnts := p.eat_comments()
+ if p.tok.kind == .ellipsis {
+ // updating init { ...base_map, 'b': 44, 'c': 55 }
+ has_update_expr = true
+ p.check(.ellipsis)
+ update_expr = p.expr(0)
+ update_expr_pos = update_expr.pos()
+ if p.tok.kind == .comma {
+ p.next()
+ }
+ update_expr_comments << p.eat_comments(same_line: true)
+ }
for p.tok.kind !in [.rcbr, .eof] {
if p.tok.kind == .name && p.tok.lit in ['r', 'c', 'js'] {
key := p.string_expr()
@@ -219,6 +234,10 @@ fn (mut p Parser) map_init() ast.MapInit {
pos: first_pos.extend_with_last_line(p.tok.pos(), p.tok.line_nr)
comments: comments
pre_cmnts: pre_cmnts
+ has_update_expr: has_update_expr
+ update_expr: update_expr
+ update_expr_pos: update_expr_pos
+ update_expr_comments: update_expr_comments
}
}
diff --git a/vlib/v/tests/map_init_with_update_test.v b/vlib/v/tests/map_init_with_update_test.v
new file mode 100644
index 00000000000000..76a350cfac0a07
--- /dev/null
+++ b/vlib/v/tests/map_init_with_update_test.v
@@ -0,0 +1,52 @@
+const base_map = {
+ 'a': 4
+ 'b': 5
+}
+
+fn test_map_init_with_update() {
+ foo := {
+ ...base_map
+ 'b': 88
+ 'c': 99
+ }
+ assert base_map.keys() == ['a', 'b']
+ assert base_map['a'] == 4
+ assert base_map['b'] == 5
+ assert foo.keys() == ['a', 'b', 'c']
+ assert foo['a'] == 4
+ assert foo['b'] == 88
+ assert foo['c'] == 99
+
+ bar := {
+ ...foo
+ 'b': 6
+ 'd': 7
+ }
+ assert base_map.keys() == ['a', 'b']
+ assert base_map['a'] == 4
+ assert base_map['b'] == 5
+ assert foo.keys() == ['a', 'b', 'c']
+ assert foo['a'] == 4
+ assert foo['b'] == 88
+ assert foo['c'] == 99
+ assert bar.keys() == ['a', 'b', 'c', 'd']
+ assert bar['a'] == 4
+ assert bar['b'] == 6
+ assert bar['c'] == 99
+ assert bar['d'] == 7
+}
+
+fn test_map_init_with_only_update() {
+ mut foo := {
+ ...base_map
+ }
+ bar := {
+ ...foo
+ }
+ foo['a'] = 99
+ foo['c'] = 99
+ assert bar.keys() == ['a', 'b']
+ assert bar['a'] == 4
+ assert bar['b'] == 5
+ assert bar == base_map
+}
|