Skip to content

Commit 2e5910b

Browse files
committed
patch 8.2.2455: Vim9: key type for literal dict and indexing is inconsistent
Problem: Vim9: key type that can be used for literal dict and indexing is inconsistent. Solution: Allow using number and bool as key for a literal dict. (#7771)
1 parent 91478ae commit 2e5910b

8 files changed

Lines changed: 74 additions & 38 deletions

File tree

runtime/doc/vim9.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,12 @@ In case the key needs to be an expression, square brackets can be used, just
548548
like in JavaScript: >
549549
var dict = {["key" .. nr]: value}
550550
551+
The key type can be string, number, bool or float. Other types result in an
552+
error. A number can be given with and without the []: >
553+
var dict = {123: 'without', [456]: 'with'}
554+
echo dict
555+
{'456': 'with', '123': 'without'}
556+
551557
552558
No :xit, :t, :append, :change or :insert ~
553559

src/dict.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -953,11 +953,13 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
953953
}
954954
if (evaluate)
955955
{
956-
if (vim9script && check_for_string(&tvkey) == FAIL)
956+
#ifdef FEAT_FLOAT
957+
if (tvkey.v_type == VAR_FLOAT)
957958
{
958-
clear_tv(&tvkey);
959-
goto failret;
959+
tvkey.vval.v_string = typval_tostring(&tvkey, TRUE);
960+
tvkey.v_type = VAR_STRING;
960961
}
962+
#endif
961963
key = tv_get_string_buf_chk(&tvkey, buf);
962964
if (key == NULL)
963965
{

src/eval.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3849,11 +3849,23 @@ eval_index(
38493849
clear_tv(&var1);
38503850
return FAIL;
38513851
}
3852-
else if (evaluate && tv_get_string_chk(&var1) == NULL)
3852+
else if (evaluate)
38533853
{
3854-
// not a number or string
3855-
clear_tv(&var1);
3856-
return FAIL;
3854+
#ifdef FEAT_FLOAT
3855+
// allow for indexing with float
3856+
if (vim9 && rettv->v_type == VAR_DICT
3857+
&& var1.v_type == VAR_FLOAT)
3858+
{
3859+
var1.vval.v_string = typval_tostring(&var1, TRUE);
3860+
var1.v_type = VAR_STRING;
3861+
}
3862+
#endif
3863+
if (tv_get_string_chk(&var1) == NULL)
3864+
{
3865+
// not a number or string
3866+
clear_tv(&var1);
3867+
return FAIL;
3868+
}
38573869
}
38583870

38593871
/*

src/testdir/test_vim9_builtin.vim

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -350,10 +350,6 @@ def Test_job_info_return_type()
350350
endif
351351
enddef
352352

353-
def Wrong_dict_key_type(items: list<number>): list<number>
354-
return filter(items, (_, val) => get({[val]: 1}, 'x'))
355-
enddef
356-
357353
def Test_filereadable()
358354
assert_false(filereadable(""))
359355
assert_false(filereadable(test_null_string()))
@@ -410,8 +406,12 @@ def Test_fnamemodify()
410406
CheckDefExecFailure(['echo fnamemodify("file", true)'], 'E928:')
411407
enddef
412408

409+
def Wrong_dict_key_type(items: list<number>): list<number>
410+
return filter(items, (_, val) => get({[val]: 1}, 'x'))
411+
enddef
412+
413413
def Test_filter_wrong_dict_key_type()
414-
assert_fails('Wrong_dict_key_type([1, 2, 3])', 'E1012:')
414+
assert_fails('Wrong_dict_key_type([1, v:null, 3])', 'E1013:')
415415
enddef
416416

417417
def Test_filter_return_type()

src/testdir/test_vim9_expr.vim

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,13 +1354,11 @@ def Test_expr5_list_add()
13541354
endfor
13551355

13561356
# concatenating two lists with different member types results in "any"
1357-
var lines =<< trim END
1358-
var d = {}
1359-
for i in ['a'] + [0]
1360-
d = {[i]: 0}
1361-
endfor
1362-
END
1363-
CheckDefExecFailure(lines, 'E1012:')
1357+
var dany = {}
1358+
for i in ['a'] + [12]
1359+
dany[i] = i
1360+
endfor
1361+
assert_equal({a: 'a', 12: 12}, dany)
13641362
enddef
13651363

13661364
" test multiply, divide, modulo
@@ -2116,6 +2114,25 @@ def Test_expr7_dict()
21162114
var cd = { # comment
21172115
key: 'val' # comment
21182116
}
2117+
2118+
# different types used for the key
2119+
var dkeys = {['key']: 'string',
2120+
[12]: 'numberexpr',
2121+
34: 'number',
2122+
[true]: 'bool'}
2123+
assert_equal('string', dkeys['key'])
2124+
assert_equal('numberexpr', dkeys[12])
2125+
assert_equal('number', dkeys[34])
2126+
assert_equal('bool', dkeys[true])
2127+
if has('float')
2128+
dkeys = {[1.2]: 'floatexpr', [3.4]: 'float'}
2129+
assert_equal('floatexpr', dkeys[1.2])
2130+
assert_equal('float', dkeys[3.4])
2131+
endif
2132+
2133+
# automatic conversion from number to string
2134+
var n = 123
2135+
var dictnr = {[n]: 1}
21192136
END
21202137
CheckDefAndScriptSuccess(lines)
21212138

@@ -2142,16 +2159,11 @@ def Test_expr7_dict()
21422159
CheckDefExecFailure(['var x: dict<string> = {a: 234, b: "1"}'], 'E1012:', 1)
21432160
CheckDefExecFailure(['var x: dict<string> = {a: "x", b: 134}'], 'E1012:', 1)
21442161

2162+
# invalid types for the key
2163+
CheckDefFailure(["var x = {[[1, 2]]: 0}"], 'E1105:', 1)
2164+
21452165
CheckDefFailure(['var x = ({'], 'E723:', 2)
21462166
CheckDefExecFailure(['{}[getftype("file")]'], 'E716: Key not present in Dictionary: ""', 1)
2147-
2148-
# no automatic conversion from number to string
2149-
lines =<< trim END
2150-
var n = 123
2151-
var d = {[n]: 1}
2152-
END
2153-
CheckDefFailure(lines, 'E1012:', 2)
2154-
CheckScriptFailure(['vim9script'] + lines, 'E928:', 3)
21552167
enddef
21562168

21572169
def Test_expr7_dict_vim9script()

src/testdir/test_vim9_script.vim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,8 +450,8 @@ def Test_try_catch_throw()
450450

451451
var nd: dict<any>
452452
try
453-
nd = {[g:anumber]: 1}
454-
catch /E1012:/
453+
nd = {[g:alist]: 1}
454+
catch /E1105:/
455455
n = 266
456456
endtry
457457
assert_equal(266, n)

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2455,
753755
/**/
754756
2454,
755757
/**/

src/vim9compile.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3145,7 +3145,6 @@ compile_lambda(char_u **arg, cctx_T *cctx)
31453145
compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
31463146
{
31473147
garray_T *instr = &cctx->ctx_instr;
3148-
garray_T *stack = &cctx->ctx_type_stack;
31493148
int count = 0;
31503149
dict_T *d = dict_alloc();
31513150
dictitem_T *item;
@@ -3180,16 +3179,19 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
31803179
if (compile_expr0(arg, cctx) == FAIL)
31813180
return FAIL;
31823181
isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
3183-
if (isn->isn_type == ISN_PUSHS)
3184-
key = isn->isn_arg.string;
3185-
else
3182+
if (isn->isn_type == ISN_PUSHNR)
31863183
{
3187-
type_T *keytype = ((type_T **)stack->ga_data)
3188-
[stack->ga_len - 1];
3189-
if (need_type(keytype, &t_string, -1, 0, cctx,
3190-
FALSE, FALSE) == FAIL)
3191-
return FAIL;
3184+
char buf[NUMBUFLEN];
3185+
3186+
// Convert to string at compile time.
3187+
vim_snprintf(buf, NUMBUFLEN, "%lld", isn->isn_arg.number);
3188+
isn->isn_type = ISN_PUSHS;
3189+
isn->isn_arg.string = vim_strsave((char_u *)buf);
31923190
}
3191+
if (isn->isn_type == ISN_PUSHS)
3192+
key = isn->isn_arg.string;
3193+
else if (may_generate_2STRING(-1, cctx) == FAIL)
3194+
return FAIL;
31933195
*arg = skipwhite(*arg);
31943196
if (**arg != ']')
31953197
{

0 commit comments

Comments
 (0)