To: vim_dev@googlegroups.com Subject: Patch 8.2.4487 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4487 Problem: Vim9: cannot compare with v:null. Solution: Allow comparing anything with v:null. (closes #9866) Files: src/vim9instr.c, src/typval.c, src/proto/typval.pro, src/vim9.h, src/vim9execute.c, src/evalvars.c, src/testdir/test_vim9_expr.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.4486/src/vim9instr.c 2022-02-06 17:16:57.730598839 +0000 --- src/vim9instr.c 2022-03-01 17:51:00.890904474 +0000 *************** *** 372,377 **** --- 372,395 ---- || ((type1 == VAR_NUMBER || type1 == VAR_FLOAT) && (type2 == VAR_NUMBER || type2 == VAR_FLOAT))) isntype = ISN_COMPAREANY; + else if (type1 == VAR_SPECIAL || type2 == VAR_SPECIAL) + { + switch (type1 == VAR_SPECIAL ? type2 : type1) + { + case VAR_BLOB: break; + case VAR_CHANNEL: break; + case VAR_DICT: break; + case VAR_FUNC: break; + case VAR_JOB: break; + case VAR_LIST: break; + case VAR_PARTIAL: break; + case VAR_STRING: break; + default: semsg(_(e_cannot_compare_str_with_str), + vartype_name(type1), vartype_name(type2)); + return ISN_DROP; + } + isntype = ISN_COMPARENULL; + } if ((exprtype == EXPR_IS || exprtype == EXPR_ISNOT) && (isntype == ISN_COMPAREBOOL *************** *** 388,394 **** && (type1 == VAR_BOOL || type1 == VAR_SPECIAL || type2 == VAR_BOOL || type2 == VAR_SPECIAL))) || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL ! && exprtype != EXPR_IS && exprtype != EXPR_ISNOT && (type1 == VAR_BLOB || type2 == VAR_BLOB || type1 == VAR_LIST || type2 == VAR_LIST)))) { --- 406,412 ---- && (type1 == VAR_BOOL || type1 == VAR_SPECIAL || type2 == VAR_BOOL || type2 == VAR_SPECIAL))) || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL ! && exprtype != EXPR_IS && exprtype != EXPR_ISNOT && (type1 == VAR_BLOB || type2 == VAR_BLOB || type1 == VAR_LIST || type2 == VAR_LIST)))) { *************** *** 2131,2136 **** --- 2149,2155 ---- case ISN_COMPAREFUNC: case ISN_COMPARELIST: case ISN_COMPARENR: + case ISN_COMPARENULL: case ISN_COMPARESPECIAL: case ISN_COMPARESTRING: case ISN_CONCAT: *** ../vim-8.2.4486/src/typval.c 2022-01-08 21:38:48.203970853 +0000 --- src/typval.c 2022-03-01 19:16:46.253253162 +0000 *************** *** 1169,1174 **** --- 1169,1189 ---- // it means TRUE. n1 = (type == EXPR_ISNOT); } + else if (((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL) + || (tv2->v_type == VAR_SPECIAL + && tv2->vval.v_number == VVAL_NULL)) + && tv1->v_type != tv2->v_type + && (type == EXPR_EQUAL || type == EXPR_NEQUAL)) + { + n1 = typval_compare_null(tv1, tv2); + if (n1 == MAYBE) + { + clear_tv(tv1); + return FAIL; + } + if (type == EXPR_NEQUAL) + n1 = !n1; + } else if (tv1->v_type == VAR_BLOB || tv2->v_type == VAR_BLOB) { if (typval_compare_blob(tv1, tv2, type, &res) == FAIL) *************** *** 1366,1371 **** --- 1381,1415 ---- } /* + * Compare v:null/v:none with another type. Return TRUE if the value is NULL. + */ + int + typval_compare_null(typval_T *tv1, typval_T *tv2) + { + if ((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL) + || (tv2->v_type == VAR_SPECIAL && tv2->vval.v_number == VVAL_NULL)) + { + typval_T *tv = tv1->v_type == VAR_SPECIAL ? tv2 : tv1; + + switch (tv->v_type) + { + case VAR_BLOB: return tv->vval.v_blob == NULL; + case VAR_CHANNEL: return tv->vval.v_channel == NULL; + case VAR_DICT: return tv->vval.v_dict == NULL; + case VAR_FUNC: return tv->vval.v_string == NULL; + case VAR_JOB: return tv->vval.v_job == NULL; + case VAR_LIST: return tv->vval.v_list == NULL; + case VAR_PARTIAL: return tv->vval.v_partial == NULL; + case VAR_STRING: return tv->vval.v_string == NULL; + default: break; + } + } + semsg(_(e_cannot_compare_str_with_str), + vartype_name(tv1->v_type), vartype_name(tv2->v_type)); + return MAYBE; + } + + /* * Compare "tv1" to "tv2" as blobs acording to "type". * Put the result, false or true, in "res". * Return FAIL and give an error message when the comparison can't be done. *** ../vim-8.2.4486/src/proto/typval.pro 2021-12-25 19:29:18.409881900 +0000 --- src/proto/typval.pro 2022-03-01 17:49:39.059010347 +0000 *************** *** 57,62 **** --- 57,63 ---- void copy_tv(typval_T *from, typval_T *to); int typval_compare(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic); int typval_compare_list(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res); + int typval_compare_null(typval_T *tv1, typval_T *tv2); int typval_compare_blob(typval_T *tv1, typval_T *tv2, exprtype_T type, int *res); int typval_compare_dict(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res); int typval_compare_func(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res); *** ../vim-8.2.4486/src/vim9.h 2022-02-13 21:51:02.388484128 +0000 --- src/vim9.h 2022-03-01 17:51:30.846865392 +0000 *************** *** 134,139 **** --- 134,140 ---- // comparative operations; isn_arg.op.op_type is exprtype_T, op_ic used ISN_COMPAREBOOL, ISN_COMPARESPECIAL, + ISN_COMPARENULL, ISN_COMPARENR, ISN_COMPAREFLOAT, ISN_COMPARESTRING, *** ../vim-8.2.4486/src/vim9execute.c 2022-02-28 20:54:58.129239044 +0000 --- src/vim9execute.c 2022-03-01 17:50:40.442931042 +0000 *************** *** 3880,3885 **** --- 3880,3904 ---- } break; + case ISN_COMPARENULL: + { + typval_T *tv1 = STACK_TV_BOT(-2); + typval_T *tv2 = STACK_TV_BOT(-1); + int res; + + res = typval_compare_null(tv1, tv2); + if (res == MAYBE) + goto on_error; + if (iptr->isn_arg.op.op_type == EXPR_NEQUAL) + res = !res; + clear_tv(tv1); + clear_tv(tv2); + --ectx->ec_stack.ga_len; + tv1->v_type = VAR_BOOL; + tv1->vval.v_number = res ? VVAL_TRUE : VVAL_FALSE; + } + break; + // Operation with two number arguments case ISN_OPNR: case ISN_COMPARENR: *************** *** 5901,5906 **** --- 5920,5926 ---- case ISN_COMPAREBOOL: case ISN_COMPARESPECIAL: + case ISN_COMPARENULL: case ISN_COMPARENR: case ISN_COMPAREFLOAT: case ISN_COMPARESTRING: *************** *** 5936,5941 **** --- 5956,5962 ---- case ISN_COMPAREBOOL: type = "COMPAREBOOL"; break; case ISN_COMPARESPECIAL: type = "COMPARESPECIAL"; break; + case ISN_COMPARENULL: type = "COMPARENULL"; break; case ISN_COMPARENR: type = "COMPARENR"; break; case ISN_COMPAREFLOAT: type = "COMPAREFLOAT"; break; case ISN_COMPARESTRING: *** ../vim-8.2.4486/src/evalvars.c 2022-02-21 15:59:07.571981059 +0000 --- src/evalvars.c 2022-03-01 18:23:56.311653905 +0000 *************** *** 2816,2844 **** } // If a list or dict variable wasn't initialized, do it now. ! if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL) { ! tv->vval.v_dict = dict_alloc(); ! if (tv->vval.v_dict != NULL) { ! ++tv->vval.v_dict->dv_refcount; ! tv->vval.v_dict->dv_type = alloc_type(type); } ! } ! else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL) ! { ! tv->vval.v_list = list_alloc(); ! if (tv->vval.v_list != NULL) { ! ++tv->vval.v_list->lv_refcount; ! 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); } --- 2816,2848 ---- } // If a list or dict variable wasn't initialized, 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) { ! tv->vval.v_dict = dict_alloc(); ! if (tv->vval.v_dict != NULL) ! { ! ++tv->vval.v_dict->dv_refcount; ! tv->vval.v_dict->dv_type = alloc_type(type); ! } } ! else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL) { ! tv->vval.v_list = list_alloc(); ! if (tv->vval.v_list != NULL) ! { ! ++tv->vval.v_list->lv_refcount; ! 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); } *** ../vim-8.2.4486/src/testdir/test_vim9_expr.vim 2022-02-28 20:54:58.129239044 +0000 --- src/testdir/test_vim9_expr.vim 2022-03-01 19:22:20.004489377 +0000 *************** *** 712,717 **** --- 712,792 ---- unlet g:notReached enddef + def Test_expr4_compare_null() + g:null_dict = test_null_dict() + g:not_null_list = [] + var lines =<< trim END + assert_true(test_null_blob() == v:null) + assert_true(v:null == test_null_blob()) + assert_false(test_null_blob() != v:null) + assert_false(v:null != test_null_blob()) + + if has('channel') + assert_true(test_null_channel() == v:null) + assert_true(v:null == test_null_channel()) + assert_false(test_null_channel() != v:null) + assert_false(v:null != test_null_channel()) + endif + + assert_true(test_null_dict() == v:null) + assert_true(v:null == test_null_dict()) + assert_false(test_null_dict() != v:null) + assert_false(v:null != test_null_dict()) + + assert_true(g:null_dict == v:null) + assert_true(v:null == g:null_dict) + assert_false(g:null_dict != v:null) + assert_false(v:null != g:null_dict) + + assert_true(test_null_function() == v:null) + assert_true(v:null == test_null_function()) + assert_false(test_null_function() != v:null) + assert_false(v:null != test_null_function()) + + if has('job') + assert_true(test_null_job() == v:null) + assert_true(v:null == test_null_job()) + assert_false(test_null_job() != v:null) + assert_false(v:null != test_null_job()) + endif + + assert_true(test_null_list() == v:null) + assert_true(v:null == test_null_list()) + assert_false(test_null_list() != v:null) + assert_false(v:null != test_null_list()) + + assert_false(g:not_null_list == v:null) + assert_false(v:null == g:not_null_list) + assert_true(g:not_null_list != v:null) + assert_true(v:null != g:not_null_list) + + assert_true(test_null_partial() == v:null) + assert_true(v:null == test_null_partial()) + assert_false(test_null_partial() != v:null) + assert_false(v:null != test_null_partial()) + + assert_true(test_null_string() == v:null) + assert_true(v:null == test_null_string()) + assert_false(test_null_string() != v:null) + assert_false(v:null != test_null_string()) + END + v9.CheckDefAndScriptSuccess(lines) + unlet g:null_dict + unlet g:not_null_list + + v9.CheckDefAndScriptFailure(['echo 123 == v:null'], 'E1072: Cannot compare number with special') + v9.CheckDefAndScriptFailure(['echo v:null == 123'], 'E1072: Cannot compare special with number') + v9.CheckDefAndScriptFailure(['echo 123 != v:null'], 'E1072: Cannot compare number with special') + v9.CheckDefAndScriptFailure(['echo v:null != 123'], 'E1072: Cannot compare special with number') + v9.CheckDefAndScriptFailure(['echo true == v:null'], 'E1072: Cannot compare bool with special') + v9.CheckDefAndScriptFailure(['echo v:null == true'], 'E1072: Cannot compare special with bool') + v9.CheckDefAndScriptFailure(['echo true != v:null'], 'E1072: Cannot compare bool with special') + v9.CheckDefAndScriptFailure(['echo v:null != true'], 'E1072: Cannot compare special with bool') + v9.CheckDefAndScriptFailure(['echo false == v:null'], 'E1072: Cannot compare bool with special') + + v9.CheckDefExecAndScriptFailure(['echo [] == v:none'], ['E1072: Cannot compare list with special', 'E691: Can only compare List with List']) + enddef + def Test_expr4_wrong_type() for op in ['>', '>=', '<', '<=', '=~', '!~'] v9.CheckDefExecAndScriptFailure([ *** ../vim-8.2.4486/src/testdir/test_vim9_disassemble.vim 2022-02-15 15:37:07.319841654 +0000 --- src/testdir/test_vim9_disassemble.vim 2022-03-01 18:34:11.790599653 +0000 *************** *** 1846,1851 **** --- 1846,1853 ---- ['true != isFalse', 'COMPAREBOOL !='], ['v:none == isNull', 'COMPARESPECIAL =='], ['v:none != isNull', 'COMPARESPECIAL !='], + ['"text" == isNull', 'COMPARENULL =='], + ['"text" != isNull', 'COMPARENULL !='], ['111 == aNumber', 'COMPARENR =='], ['111 != aNumber', 'COMPARENR !='], *** ../vim-8.2.4486/src/version.c 2022-03-01 16:00:03.131398059 +0000 --- src/version.c 2022-03-01 17:28:24.576164421 +0000 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 4487, /**/ -- Birthdays are healthy. The more you have them, the longer you live. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///