diff --git a/vlib/builtin/map_test.v b/vlib/builtin/map_test.v index e951680a5d..7bc77e9239 100644 --- a/vlib/builtin/map_test.v +++ b/vlib/builtin/map_test.v @@ -485,6 +485,8 @@ fn test_int_keys() { m[5] += 24 m[5]++ assert m[5] == 25 + m2 := {3:9 4:16 5:25} + assert m2.len == 3 mc := m.clone() assert mc.len == 3 mut all := []int{} @@ -506,6 +508,21 @@ fn test_voidptr_keys() { assert m.len == 2 } +fn test_rune_keys() { + mut m := {`!`:2 `%`:3} + assert typeof(m).name == 'map[rune]int' + assert m[`!`] == 2 + m[`@`] = 7 + assert m.len == 3 + + mut a := []rune{} + for k, v in m { + a << k + a << rune(v) + `0` + } + assert a == [`!`, `2`, `%`, `3`, `@`, `7`] +} + fn test_eq() { a := { 'a': 1 diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 9b92b25aaa..6b7f5b8755 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4656,6 +4656,25 @@ pub fn (mut c Checker) chan_init(mut node ast.ChanInit) table.Type { } } +pub fn (mut c Checker) check_dup_keys(node &ast.MapInit, i int) { + key_i := node.keys[i] + if key_i is ast.StringLiteral { + for j in 0 .. i { + key_j := node.keys[j] as ast.StringLiteral + if key_i.val == key_j.val { + c.error('duplicate key "$key_i.val" in map literal', key_i.pos) + } + } + } else if key_i is ast.IntegerLiteral { + for j in 0 .. i { + key_j := node.keys[j] as ast.IntegerLiteral + if key_i.val == key_j.val { + c.error('duplicate key "$key_i.val" in map literal', key_i.pos) + } + } + } +} + pub fn (mut c Checker) map_init(mut node ast.MapInit) table.Type { // `x := map[string]string` - set in parser if node.typ != 0 { @@ -4675,14 +4694,8 @@ pub fn (mut c Checker) map_init(mut node ast.MapInit) table.Type { // `{'age': 20}` key0_type := c.table.mktyp(c.expr(node.keys[0])) val0_type := c.table.mktyp(c.expr(node.vals[0])) + mut same_key_type := true for i, key in node.keys { - key_i := key as ast.StringLiteral - for j in 0 .. i { - key_j := node.keys[j] as ast.StringLiteral - if key_i.val == key_j.val { - c.error('duplicate key "$key_i.val" in map literal', key.position()) - } - } if i == 0 { continue } @@ -4690,16 +4703,18 @@ pub fn (mut c Checker) map_init(mut node ast.MapInit) table.Type { key_type := c.expr(key) val_type := c.expr(val) if !c.check_types(key_type, key0_type) { - key0_type_sym := c.table.get_type_symbol(key0_type) - key_type_sym := c.table.get_type_symbol(key_type) - c.error('map init: cannot use `$key_type_sym.name` as `$key0_type_sym.name` for map key', - node.pos) + msg := c.expected_msg(key_type, key0_type) + c.error('invalid map key: $msg', key.position()) + same_key_type = false } if !c.check_types(val_type, val0_type) { - val0_type_sym := c.table.get_type_symbol(val0_type) - val_type_sym := c.table.get_type_symbol(val_type) - c.error('map init: cannot use `$val_type_sym.name` as `$val0_type_sym.name` for map value', - node.pos) + msg := c.expected_msg(val_type, val0_type) + c.error('invalid map value: $msg', val.position()) + } + } + if same_key_type { + for i in 1 .. node.keys.len { + c.check_dup_keys(node, i) } } map_type := table.new_type(c.table.find_or_register_map(key0_type, val0_type)) diff --git a/vlib/v/checker/tests/map_init_key_duplicate_err.out b/vlib/v/checker/tests/map_init_key_duplicate_err.out index 975a814458..5798002c82 100644 --- a/vlib/v/checker/tests/map_init_key_duplicate_err.out +++ b/vlib/v/checker/tests/map_init_key_duplicate_err.out @@ -5,3 +5,9 @@ vlib/v/checker/tests/map_init_key_duplicate_err.vv:5:3: error: duplicate key "fo | ~~~~~ 6 | } 7 | println(a) +vlib/v/checker/tests/map_init_key_duplicate_err.vv:9:15: error: duplicate key "2" in map literal + 7 | println(a) + 8 | + 9 | _ = {2:0 3:0 2:0} + | ^ + 10 | } diff --git a/vlib/v/checker/tests/map_init_key_duplicate_err.vv b/vlib/v/checker/tests/map_init_key_duplicate_err.vv index 497c61a46b..952c003726 100644 --- a/vlib/v/checker/tests/map_init_key_duplicate_err.vv +++ b/vlib/v/checker/tests/map_init_key_duplicate_err.vv @@ -5,4 +5,6 @@ fn main() { 'foo': 'bar' } println(a) + + _ = {2:0 3:0 2:0} } diff --git a/vlib/v/checker/tests/map_init_wrong_type.out b/vlib/v/checker/tests/map_init_wrong_type.out index c49ce4d904..c15c9fdcaf 100644 --- a/vlib/v/checker/tests/map_init_wrong_type.out +++ b/vlib/v/checker/tests/map_init_wrong_type.out @@ -3,4 +3,18 @@ vlib/v/checker/tests/map_init_wrong_type.vv:3:10: error: cannot assign to `a`: e 2 | mut a := map[string]f32{} 3 | a = { 'x': 12.3 } | ~~~ - 4 | } + 4 | _ = {2:0 3:0 "hi":0} + 5 | _ = {2:0 3:`@` 4:0} +vlib/v/checker/tests/map_init_wrong_type.vv:4:17: error: invalid map key: expected `int`, not `string` + 2 | mut a := map[string]f32{} + 3 | a = { 'x': 12.3 } + 4 | _ = {2:0 3:0 "hi":0} + | ~~~~ + 5 | _ = {2:0 3:`@` 4:0} + 6 | } +vlib/v/checker/tests/map_init_wrong_type.vv:5:15: error: invalid map value: expected `int`, not `rune` + 3 | a = { 'x': 12.3 } + 4 | _ = {2:0 3:0 "hi":0} + 5 | _ = {2:0 3:`@` 4:0} + | ~~~ + 6 | } diff --git a/vlib/v/checker/tests/map_init_wrong_type.vv b/vlib/v/checker/tests/map_init_wrong_type.vv index 273509d43d..fb77120b14 100644 --- a/vlib/v/checker/tests/map_init_wrong_type.vv +++ b/vlib/v/checker/tests/map_init_wrong_type.vv @@ -1,4 +1,6 @@ fn main() { mut a := map[string]f32{} a = { 'x': 12.3 } + _ = {2:0 3:0 "hi":0} + _ = {2:0 3:`@` 4:0} } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index b67b9c5a45..8d210c5b29 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -2302,7 +2302,7 @@ fn (mut g Gen) map_fn_ptrs(key_typ table.TypeSymbol) (string, string, string, st key_eq_fn = '&map_eq_int_2' clone_fn = '&map_clone_int_2' } - .int, .u32 { + .int, .u32, .rune { hash_fn = '&map_hash_int_4' key_eq_fn = '&map_eq_int_4' clone_fn = '&map_clone_int_4' diff --git a/vlib/v/parser/containers.v b/vlib/v/parser/containers.v index bdc3ffc331..20af0c877b 100644 --- a/vlib/v/parser/containers.v +++ b/vlib/v/parser/containers.v @@ -152,8 +152,10 @@ fn (mut p Parser) map_init() ast.MapInit { mut keys := []ast.Expr{} mut vals := []ast.Expr{} for p.tok.kind != .rcbr && p.tok.kind != .eof { - // p.check(.str) key := p.expr(0) + if key is ast.FloatLiteral { + p.error_with_pos('maps do not support floating point keys yet', key.pos) + } keys << key p.check(.colon) val := p.expr(0) diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index c19ab55133..e8fed08b15 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -193,7 +193,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { .lcbr { // Map `{"age": 20}` or `{ x | foo:bar, a:10 }` p.next() - if p.tok.kind == .string { + if p.tok.kind in [.chartoken, .number, .string] { node = p.map_init() } else { // it should be a struct diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index 8bf8f4fadc..470e7d561e 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -21,7 +21,7 @@ pub enum Kind { number // 123 string // 'foo' str_inter // 'name=$user.name' - chartoken // `A` + chartoken // `A` - rune plus minus mul