# HG changeset patch # User Bram Moolenaar # Date 1646768703 -3600 # Node ID cc7d54a134e4835ba9d79b50368213a15f369633 # Parent e11bea0167bb30239951b66f488e0c52c1c79b4e patch 8.2.4529: Vim9: comparing partial with function fails Commit: https://github.com/vim/vim/commit/ed0c62e7b16b62655824df28cdd6bd75aadbb8fc Author: Bram Moolenaar Date: Tue Mar 8 19:43:55 2022 +0000 patch 8.2.4529: Vim9: comparing partial with function fails Problem: Vim9: comparing partial with function fails. Solution: Support this comparison. Avoid a crash. (closes https://github.com/vim/vim/issues/9909) Add more test cases. diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -1687,6 +1687,7 @@ def Test_getenv() endif $SOMEENVVAR = 'some' assert_equal('some', getenv('SOMEENVVAR')) + assert_notequal(null, getenv('SOMEENVVAR')) unlet $SOMEENVVAR getenv('')->assert_equal(v:null) enddef @@ -4398,7 +4399,7 @@ def Test_typename() if has('float') assert_equal('func([unknown], [unknown]): float', typename(function('pow'))) endif - assert_equal('func', test_null_partial()->typename()) + assert_equal('func(...): unknown', test_null_partial()->typename()) assert_equal('list', test_null_list()->typename()) assert_equal('dict', test_null_dict()->typename()) if has('job') diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -717,21 +717,33 @@ def Test_expr4_compare_null() g:not_null_list = [] var lines =<< trim END assert_true(test_null_blob() == v:null) + assert_true(null_blob == null) assert_true(v:null == test_null_blob()) + assert_true(null == null_blob) assert_false(test_null_blob() != v:null) + assert_false(null_blob != null) assert_false(v:null != test_null_blob()) + assert_false(null != null_blob) if has('channel') assert_true(test_null_channel() == v:null) + assert_true(null_channel == null) assert_true(v:null == test_null_channel()) + assert_true(null == null_channel) assert_false(test_null_channel() != v:null) + assert_false(null_channel != null) assert_false(v:null != test_null_channel()) + assert_false(null != null_channel) endif assert_true(test_null_dict() == v:null) + assert_true(null_dict == null) assert_true(v:null == test_null_dict()) + assert_true(null == null_dict) assert_false(test_null_dict() != v:null) + assert_false(null_dict != null) assert_false(v:null != test_null_dict()) + assert_false(null != null_dict) assert_true(g:null_dict == v:null) assert_true(v:null == g:null_dict) @@ -739,21 +751,33 @@ def Test_expr4_compare_null() assert_false(v:null != g:null_dict) assert_true(test_null_function() == v:null) + assert_true(null_function == null) assert_true(v:null == test_null_function()) + assert_true(null == null_function) assert_false(test_null_function() != v:null) + assert_false(null_function != null) assert_false(v:null != test_null_function()) + assert_false(null != null_function) if has('job') assert_true(test_null_job() == v:null) + assert_true(null_job == null) assert_true(v:null == test_null_job()) + assert_true(null == null_job) assert_false(test_null_job() != v:null) + assert_false(null_job != null) assert_false(v:null != test_null_job()) + assert_false(null != null_job) endif assert_true(test_null_list() == v:null) + assert_true(null_list == null) assert_true(v:null == test_null_list()) + assert_true(null == null_list) assert_false(test_null_list() != v:null) + assert_false(null_list != null) assert_false(v:null != test_null_list()) + assert_false(null != null_list) assert_false(g:not_null_list == v:null) assert_false(v:null == g:not_null_list) @@ -761,19 +785,33 @@ def Test_expr4_compare_null() assert_true(v:null != g:not_null_list) assert_true(test_null_partial() == v:null) + assert_true(null_partial == null) assert_true(v:null == test_null_partial()) + assert_true(null == null_partial) assert_false(test_null_partial() != v:null) + assert_false(null_partial != null) assert_false(v:null != test_null_partial()) + assert_false(null != null_partial) assert_true(test_null_string() == v:null) + assert_true(null_string == null) assert_true(v:null == test_null_string()) + assert_true(null == null_string) assert_false(test_null_string() != v:null) + assert_false(null_string != null) assert_false(v:null != test_null_string()) + assert_false(null != null_string) END v9.CheckDefAndScriptSuccess(lines) unlet g:null_dict unlet g:not_null_list + lines =<< trim END + var d: dict = {f: null_function} + assert_equal(null_function, d.f) + END + v9.CheckDefAndScriptSuccess(lines) + 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') diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -3341,7 +3341,7 @@ def Test_partial_null_function() var lines =<< trim END var d: dict = {f: null_function} var Ref = d.f - assert_equal('func', typename(Ref)) + assert_equal('func(...): unknown', typename(Ref)) END v9.CheckDefAndScriptSuccess(lines) enddef diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim --- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -6571,6 +6571,9 @@ func Test_type() call assert_false(v:true is 1) call assert_false(v:true is v:false) call assert_false(v:none is 0) + call assert_false(v:none is []) + call assert_false(v:none is {}) + call assert_false(v:none is 'text') call assert_false(v:null is 0) call assert_false(v:null is v:none) diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -5730,18 +5730,27 @@ func_has_abort( make_partial(dict_T *selfdict_in, typval_T *rettv) { char_u *fname; - ufunc_T *fp; + ufunc_T *fp = NULL; char_u fname_buf[FLEN_FIXED + 1]; int error; dict_T *selfdict = selfdict_in; - if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial->pt_func != NULL) + if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial != NULL + && rettv->vval.v_partial->pt_func != NULL) fp = rettv->vval.v_partial->pt_func; else { fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string + : rettv->vval.v_partial == NULL ? NULL : rettv->vval.v_partial->pt_name; - if (fname != NULL) + if (fname == NULL) + { + // There is no point binding a dict to a NULL function, just create + // a function reference. + rettv->v_type = VAR_FUNC; + rettv->vval.v_string = NULL; + } + else { char_u *tofree = NULL; @@ -5752,8 +5761,7 @@ make_partial(dict_T *selfdict_in, typval } } - if ((fp != NULL && (fp->uf_flags & FC_DICT)) - || (rettv->v_type == VAR_FUNC && rettv->vval.v_string == NULL)) + if (fp != NULL && (fp->uf_flags & FC_DICT)) { partial_T *pt = ALLOC_CLEAR_ONE(partial_T); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4529, +/**/ 4528, /**/ 4527, diff --git a/src/vim9instr.c b/src/vim9instr.c --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -370,7 +370,9 @@ get_compare_isn(exprtype_T exprtype, var } else if (type1 == VAR_ANY || type2 == VAR_ANY || ((type1 == VAR_NUMBER || type1 == VAR_FLOAT) - && (type2 == VAR_NUMBER || type2 == VAR_FLOAT))) + && (type2 == VAR_NUMBER || type2 == VAR_FLOAT)) + || (type1 == VAR_FUNC && type2 == VAR_PARTIAL) + || (type1 == VAR_PARTIAL && type2 == VAR_FUNC)) isntype = ISN_COMPAREANY; else if (type1 == VAR_SPECIAL || type2 == VAR_SPECIAL) { diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -420,6 +420,8 @@ typval2type_int(typval_T *tv, int copyID } else name = tv->vval.v_string; + if (name == NULL && ufunc == NULL) + return &t_func_unknown; if (name != NULL) { int idx = find_internal_func(name);