From ec15b1cfdc5faadb529dedda58adf7fc98c839ed Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 27 Mar 2022 16:29:53 +0100 Subject: patch 8.2.4634: Vim9: cannot initialize a variable to null_list Problem: Vim9: cannot initialize a variable to null_list. Solution: Give negative count to NEWLIST. (closes #10027) Also fix inconsistencies in comparing with null values. --- src/evalvars.c | 17 ++-- src/proto/vim9instr.pro | 4 +- src/testdir/test_vim9_builtin.vim | 4 +- src/testdir/test_vim9_disassemble.vim | 4 +- src/testdir/test_vim9_expr.vim | 48 ++++++++++++ src/typval.c | 15 +++- src/version.c | 2 + src/vim9.h | 2 + src/vim9compile.c | 6 +- src/vim9execute.c | 141 +++++++++++++++++++++------------- src/vim9expr.c | 4 +- src/vim9instr.c | 16 ++-- 12 files changed, 179 insertions(+), 84 deletions(-) diff --git a/src/evalvars.c b/src/evalvars.c index 058e8048f..19a0f3389 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -2816,11 +2816,13 @@ eval_variable( type = sv->sv_type; } - // If a list or dict variable wasn't initialized, do it now. - // Not for global variables, they are not declared. + // If a list or dict variable wasn't initialized and has meaningful + // type, do it now. Not for global variables, they are not + // declared. if (ht != &globvarht) { - if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL) + if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL + && type != NULL && type != &t_dict_empty) { tv->vval.v_dict = dict_alloc(); if (tv->vval.v_dict != NULL) @@ -2829,7 +2831,8 @@ eval_variable( tv->vval.v_dict->dv_type = alloc_type(type); } } - else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL) + else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL + && type != NULL && type != &t_list_empty) { tv->vval.v_list = list_alloc(); if (tv->vval.v_list != NULL) @@ -2838,12 +2841,6 @@ eval_variable( tv->vval.v_list->lv_type = alloc_type(type); } } - else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL) - { - tv->vval.v_blob = blob_alloc(); - if (tv->vval.v_blob != NULL) - ++tv->vval.v_blob->bv_refcount; - } } copy_tv(tv, rettv); } diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro index 76f3b2102..52a7c79f1 100644 --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -36,8 +36,8 @@ int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit); int generate_LOCKCONST(cctx_T *cctx); int generate_OLDSCRIPT(cctx_T *cctx, isntype_T isn_type, char_u *name, int sid, type_T *type); int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type_T *type); -int generate_NEWLIST(cctx_T *cctx, int count); -int generate_NEWDICT(cctx_T *cctx, int count); +int generate_NEWLIST(cctx_T *cctx, int count, int use_null); +int generate_NEWDICT(cctx_T *cctx, int count, int use_null); int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp); int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name); int generate_DEF(cctx_T *cctx, char_u *name, size_t len); diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 8b9dbd605..24a7f9583 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -122,13 +122,13 @@ def Test_add_blob() END v9.CheckDefExecFailure(lines, 'E1131:', 2) - # Getting variable with NULL blob allocates a new blob at script level + # Getting variable with NULL blob fails lines =<< trim END vim9script var b: blob = test_null_blob() add(b, 123) END - v9.CheckScriptSuccess(lines) + v9.CheckScriptFailure(lines, 'E1131:', 3) enddef def Test_add_list() diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 77c467588..7e3021333 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -439,11 +439,11 @@ if has('job') '\d\+ STORE $\d\_s*' .. 'var dd = null_dict\_s*' .. - '\d\+ NEWDICT size 0\_s*' .. + '\d\+ NEWDICT size -1\_s*' .. '\d\+ STORE $\d\_s*' .. 'var ll = null_list\_s*' .. - '\d\+ NEWLIST size 0\_s*' .. + '\d\+ NEWLIST size -1\_s*' .. '\d\+ STORE $\d\_s*' .. 'var Ff = null_function\_s*' .. diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 41e398aea..209bb10a5 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -754,6 +754,12 @@ def Test_expr4_compare_null() assert_false(v:null != test_null_blob()) assert_false(null != null_blob) + var nb = null_blob + assert_true(nb == null_blob) + assert_true(nb == null) + assert_true(null_blob == nb) + assert_true(null == nb) + if has('channel') assert_true(test_null_channel() == v:null) assert_true(null_channel == null) @@ -763,6 +769,12 @@ def Test_expr4_compare_null() assert_false(null_channel != null) assert_false(v:null != test_null_channel()) assert_false(null != null_channel) + + var nc = null_channel + assert_true(nc == null_channel) + assert_true(nc == null) + assert_true(null_channel == nc) + assert_true(null == nc) endif assert_true(test_null_dict() == v:null) @@ -779,6 +791,12 @@ def Test_expr4_compare_null() assert_false(g:null_dict != v:null) assert_false(v:null != g:null_dict) + var nd = null_dict + assert_true(nd == null_dict) + assert_true(nd == null) + assert_true(null_dict == nd) + assert_true(null == nd) + assert_true(test_null_function() == v:null) assert_true(null_function == null) assert_true(v:null == test_null_function()) @@ -788,6 +806,12 @@ def Test_expr4_compare_null() assert_false(v:null != test_null_function()) assert_false(null != null_function) + var Nf = null_function + assert_true(Nf == null_function) + assert_true(Nf == null) + assert_true(null_function == Nf) + assert_true(null == Nf) + if has('job') assert_true(test_null_job() == v:null) assert_true(null_job == null) @@ -797,6 +821,12 @@ def Test_expr4_compare_null() assert_false(null_job != null) assert_false(v:null != test_null_job()) assert_false(null != null_job) + + var nj = null_job + assert_true(nj == null_job) + assert_true(nj == null) + assert_true(null_job == nj) + assert_true(null == nj) endif assert_true(test_null_list() == v:null) @@ -813,6 +843,12 @@ def Test_expr4_compare_null() assert_true(g:not_null_list != v:null) assert_true(v:null != g:not_null_list) + var nl = null_list + assert_true(nl == null_list) + assert_true(nl == null) + assert_true(null_list == nl) + assert_true(null == nl) + assert_true(test_null_partial() == v:null) assert_true(null_partial == null) assert_true(v:null == test_null_partial()) @@ -822,6 +858,12 @@ def Test_expr4_compare_null() assert_false(v:null != test_null_partial()) assert_false(null != null_partial) + var Np = null_partial + assert_true(Np == null_partial) + assert_true(Np == null) + assert_true(null_partial == Np) + assert_true(null == Np) + assert_true(test_null_string() == v:null) assert_true(null_string == null) assert_true(v:null == test_null_string()) @@ -837,6 +879,12 @@ def Test_expr4_compare_null() assert_false(null_string isnot test_null_string()) assert_true(null_string isnot '') assert_true('' isnot null_string) + + var ns = null_string + assert_true(ns == null_string) + assert_true(ns == null) + assert_true(null_string == ns) + assert_true(null == ns) END v9.CheckDefAndScriptSuccess(lines) unlet g:null_dict diff --git a/src/typval.c b/src/typval.c index d0c937e5d..50b1d620a 100644 --- a/src/typval.c +++ b/src/typval.c @@ -1314,6 +1314,19 @@ typval_compare( return FAIL; } } +#ifdef FEAT_JOB_CHANNEL + else if (tv1->v_type == tv2->v_type + && (tv1->v_type == VAR_CHANNEL || tv1->v_type == VAR_JOB) + && (type == EXPR_NEQUAL || type == EXPR_EQUAL)) + { + if (tv1->v_type == VAR_CHANNEL) + n1 = tv1->vval.v_channel == tv2->vval.v_channel; + else + n1 = tv1->vval.v_job == tv2->vval.v_job; + if (type == EXPR_NEQUAL) + n1 = !n1; + } +#endif else { if (typval_compare_string(tv1, tv2, type, ic, &res) == FAIL) @@ -1417,7 +1430,7 @@ typval_compare_null(typval_T *tv1, typval_T *tv2) default: break; } } - // although comparing null with number, float or bool is not very usefule + // although comparing null with number, float or bool is not very useful // we won't give an error return FALSE; } diff --git a/src/version.c b/src/version.c index 169468b0e..6986aaa57 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4634, /**/ 4633, /**/ diff --git a/src/vim9.h b/src/vim9.h index ed5f67695..60c54d9f5 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -90,7 +90,9 @@ typedef enum { ISN_PUSHCHANNEL, // push NULL channel ISN_PUSHJOB, // push NULL job ISN_NEWLIST, // push list from stack items, size is isn_arg.number + // -1 for null_list ISN_NEWDICT, // push dict from stack items, size is isn_arg.number + // -1 for null_dict ISN_NEWPARTIAL, // push NULL partial ISN_AUTOLOAD, // get item from autoload import, function or variable diff --git a/src/vim9compile.c b/src/vim9compile.c index 1eb6ce22f..4e3a1bfb1 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1955,7 +1955,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) generate_PUSHS(cctx, &li->li_tv.vval.v_string); li->li_tv.vval.v_string = NULL; } - generate_NEWLIST(cctx, l->lv_len); + generate_NEWLIST(cctx, l->lv_len, FALSE); } list_free(l); p += STRLEN(p); @@ -2239,10 +2239,10 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) generate_PUSHFUNC(cctx, NULL, &t_func_void); break; case VAR_LIST: - generate_NEWLIST(cctx, 0); + generate_NEWLIST(cctx, 0, FALSE); break; case VAR_DICT: - generate_NEWDICT(cctx, 0); + generate_NEWDICT(cctx, 0, FALSE); break; case VAR_JOB: generate_PUSHJOB(cctx); diff --git a/src/vim9execute.c b/src/vim9execute.c index 2d6a3fead..0404eb412 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -122,29 +122,103 @@ ufunc_argcount(ufunc_T *ufunc) /* * Create a new list from "count" items at the bottom of the stack. * When "count" is zero an empty list is added to the stack. + * When "count" is -1 a NULL list is added to the stack. */ static int exe_newlist(int count, ectx_T *ectx) { - list_T *list = list_alloc_with_items(count); + list_T *list = NULL; int idx; typval_T *tv; - if (list == NULL) - return FAIL; - for (idx = 0; idx < count; ++idx) - list_set_item(list, idx, STACK_TV_BOT(idx - count)); + if (count >= 0) + { + list = list_alloc_with_items(count); + if (list == NULL) + return FAIL; + for (idx = 0; idx < count; ++idx) + list_set_item(list, idx, STACK_TV_BOT(idx - count)); + } if (count > 0) ectx->ec_stack.ga_len -= count - 1; else if (GA_GROW_FAILS(&ectx->ec_stack, 1)) + { + list_unref(list); return FAIL; + } else ++ectx->ec_stack.ga_len; tv = STACK_TV_BOT(-1); tv->v_type = VAR_LIST; tv->vval.v_list = list; - ++list->lv_refcount; + if (list != NULL) + ++list->lv_refcount; + return OK; +} + +/* + * Implementation of ISN_NEWDICT. + * Returns FAIL on total failure, MAYBE on error. + */ + static int +exe_newdict(int count, ectx_T *ectx) +{ + dict_T *dict = NULL; + dictitem_T *item; + char_u *key; + int idx; + typval_T *tv; + + if (count >= 0) + { + dict = dict_alloc(); + if (unlikely(dict == NULL)) + return FAIL; + for (idx = 0; idx < count; ++idx) + { + // have already checked key type is VAR_STRING + tv = STACK_TV_BOT(2 * (idx - count)); + // check key is unique + key = tv->vval.v_string == NULL + ? (char_u *)"" : tv->vval.v_string; + item = dict_find(dict, key, -1); + if (item != NULL) + { + semsg(_(e_duplicate_key_in_dicitonary), key); + dict_unref(dict); + return MAYBE; + } + item = dictitem_alloc(key); + clear_tv(tv); + if (unlikely(item == NULL)) + { + dict_unref(dict); + return FAIL; + } + item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1); + item->di_tv.v_lock = 0; + if (dict_add(dict, item) == FAIL) + { + // can this ever happen? + dict_unref(dict); + return FAIL; + } + } + } + + if (count > 0) + ectx->ec_stack.ga_len -= 2 * count - 1; + else if (GA_GROW_FAILS(&ectx->ec_stack, 1)) + return FAIL; + else + ++ectx->ec_stack.ga_len; + tv = STACK_TV_BOT(-1); + tv->v_type = VAR_DICT; + tv->v_lock = 0; + tv->vval.v_dict = dict; + if (dict != NULL) + ++dict->dv_refcount; return OK; } @@ -3357,57 +3431,14 @@ exec_instructions(ectx_T *ectx) // create a dict from items on the stack case ISN_NEWDICT: { - int count = iptr->isn_arg.number; - dict_T *dict = dict_alloc(); - dictitem_T *item; - char_u *key; - int idx; - - if (unlikely(dict == NULL)) - goto theend; - for (idx = 0; idx < count; ++idx) - { - // have already checked key type is VAR_STRING - tv = STACK_TV_BOT(2 * (idx - count)); - // check key is unique - key = tv->vval.v_string == NULL - ? (char_u *)"" : tv->vval.v_string; - item = dict_find(dict, key, -1); - if (item != NULL) - { - SOURCING_LNUM = iptr->isn_lnum; - semsg(_(e_duplicate_key_in_dicitonary), key); - dict_unref(dict); - goto on_error; - } - item = dictitem_alloc(key); - clear_tv(tv); - if (unlikely(item == NULL)) - { - dict_unref(dict); - goto theend; - } - item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1); - item->di_tv.v_lock = 0; - if (dict_add(dict, item) == FAIL) - { - // can this ever happen? - dict_unref(dict); - goto theend; - } - } + int res; - if (count > 0) - ectx->ec_stack.ga_len -= 2 * count - 1; - else if (GA_GROW_FAILS(&ectx->ec_stack, 1)) + SOURCING_LNUM = iptr->isn_lnum; + res = exe_newdict(iptr->isn_arg.number, ectx); + if (res == FAIL) goto theend; - else - ++ectx->ec_stack.ga_len; - tv = STACK_TV_BOT(-1); - tv->v_type = VAR_DICT; - tv->v_lock = 0; - tv->vval.v_dict = dict; - ++dict->dv_refcount; + if (res == MAYBE) + goto on_error; } break; diff --git a/src/vim9expr.c b/src/vim9expr.c index 8c7d0b0e9..90d7c8cf4 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -958,7 +958,7 @@ compile_list(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) *arg = p; ppconst->pp_is_const = is_all_const; - return generate_NEWLIST(cctx, count); + return generate_NEWLIST(cctx, count, FALSE); } /* @@ -1246,7 +1246,7 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) dict_unref(d); ppconst->pp_is_const = is_all_const; - return generate_NEWDICT(cctx, count); + return generate_NEWDICT(cctx, count, FALSE); failret: if (*arg == NULL) diff --git a/src/vim9instr.c b/src/vim9instr.c index f0206211d..57943b8a5 100644 --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -581,12 +581,12 @@ generate_tv_PUSH(cctx_T *cctx, typval_T *tv) case VAR_LIST: if (tv->vval.v_list != NULL) iemsg("non-empty list constant not supported"); - generate_NEWLIST(cctx, 0); + generate_NEWLIST(cctx, 0, TRUE); break; case VAR_DICT: if (tv->vval.v_dict != NULL) iemsg("non-empty dict constant not supported"); - generate_NEWDICT(cctx, 0); + generate_NEWDICT(cctx, 0, TRUE); break; #ifdef FEAT_JOB_CHANNEL case VAR_JOB: @@ -1115,10 +1115,11 @@ generate_VIM9SCRIPT( } /* - * Generate an ISN_NEWLIST instruction. + * Generate an ISN_NEWLIST instruction for "count" items. + * "use_null" is TRUE for null_list. */ int -generate_NEWLIST(cctx_T *cctx, int count) +generate_NEWLIST(cctx_T *cctx, int count, int use_null) { isn_T *isn; type_T *member_type; @@ -1128,7 +1129,7 @@ generate_NEWLIST(cctx_T *cctx, int count) RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_NEWLIST)) == NULL) return FAIL; - isn->isn_arg.number = count; + isn->isn_arg.number = use_null ? -1 : count; // Get the member type and the declared member type from all the items on // the stack. @@ -1145,9 +1146,10 @@ generate_NEWLIST(cctx_T *cctx, int count) /* * Generate an ISN_NEWDICT instruction. + * "use_null" is TRUE for null_dict. */ int -generate_NEWDICT(cctx_T *cctx, int count) +generate_NEWDICT(cctx_T *cctx, int count, int use_null) { isn_T *isn; type_T *member_type; @@ -1157,7 +1159,7 @@ generate_NEWDICT(cctx_T *cctx, int count) RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_NEWDICT)) == NULL) return FAIL; - isn->isn_arg.number = count; + isn->isn_arg.number = use_null ? -1 : count; member_type = get_member_type_from_stack(count, 2, cctx); type = get_dict_type(member_type, cctx->ctx_type_list); -- cgit v1.2.1