# HG changeset patch # User Bram Moolenaar # Date 1646940604 -3600 # Node ID 0ed38a4e028d092722d960135ad5c75957bad9a3 # Parent db86841e84190140fcbcbe37cef532d2861a73ec patch 8.2.4539: when comparing special v:none and v:null are handled the same Commit: https://github.com/vim/vim/commit/53ba6ca5b27248680e368340707ad4b32a82591f Author: Bram Moolenaar Date: Thu Mar 10 19:23:28 2022 +0000 patch 8.2.4539: when comparing special v:none and v:null are handled the same Problem: When comparing special v:none and v:null are handled the same when compiling. Solution: Pass more information so that v:none can be handled differently at compile time. (issue #9923) diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -398,7 +398,8 @@ EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, TTFLAG_STATIC, NULL, NULL); EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, TTFLAG_STATIC, NULL, NULL); -EXTERN type_T t_special INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL); +EXTERN type_T t_null INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL); +EXTERN type_T t_none INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL); EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC, NULL, NULL); EXTERN type_T t_number_bool INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC|TTFLAG_BOOL_OK, NULL, NULL); EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, TTFLAG_STATIC, NULL, NULL); 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 @@ -828,8 +828,22 @@ def Test_expr4_compare_null() 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_compare_none() + var lines =<< trim END + assert_false('' == v:none) + assert_false('text' == v:none) + assert_true(v:none == v:none) + assert_false(v:none == '') + assert_false(v:none == 'text') + assert_true(v:none == v:none) + END + v9.CheckDefAndScriptSuccess(lines) + + v9.CheckDefAndScriptFailure(['echo [] == v:none'], 'E1072: Cannot compare list with special') + v9.CheckDefAndScriptFailure(['echo 123 == v:none'], 'E1072: Cannot compare number with special') + v9.CheckDefAndScriptFailure(['echo 0z00 == v:none'], 'E1072: Cannot compare blob with special') enddef def Test_expr4_wrong_type() 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 */ /**/ + 4539, +/**/ 4538, /**/ 4537, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1799,7 +1799,7 @@ compile_assign_unlet( return FAIL; } type = get_type_on_stack(cctx, 0); - if ((dest_type != VAR_BLOB && type != &t_special) + if ((dest_type != VAR_BLOB && type->tt_type != VAR_SPECIAL) && need_type(type, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; diff --git a/src/vim9instr.c b/src/vim9instr.c --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -339,22 +339,30 @@ generate_two_op(cctx_T *cctx, char_u *op } /* - * Get the instruction to use for comparing "type1" with "type2" + * Get the instruction to use for comparing two values with specified types. + * Either "tv1" and "tv2" are passed or "type1" and "type2". * Return ISN_DROP when failed. */ static isntype_T -get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2) +get_compare_isn( + exprtype_T exprtype, + typval_T *tv1, + typval_T *tv2, + type_T *type1, + type_T *type2) { isntype_T isntype = ISN_DROP; + vartype_T vartype1 = tv1 != NULL ? tv1->v_type : type1->tt_type; + vartype_T vartype2 = tv2 != NULL ? tv2->v_type : type2->tt_type; - if (type1 == VAR_UNKNOWN) - type1 = VAR_ANY; - if (type2 == VAR_UNKNOWN) - type2 = VAR_ANY; + if (vartype1 == VAR_UNKNOWN) + vartype1 = VAR_ANY; + if (vartype2 == VAR_UNKNOWN) + vartype2 = VAR_ANY; - if (type1 == type2) + if (vartype1 == vartype2) { - switch (type1) + switch (vartype1) { case VAR_BOOL: isntype = ISN_COMPAREBOOL; break; case VAR_SPECIAL: isntype = ISN_COMPARESPECIAL; break; @@ -368,15 +376,28 @@ get_compare_isn(exprtype_T exprtype, var default: isntype = ISN_COMPAREANY; break; } } - else if (type1 == VAR_ANY || type2 == VAR_ANY - || ((type1 == VAR_NUMBER || type1 == VAR_FLOAT) - && (type2 == VAR_NUMBER || type2 == VAR_FLOAT)) - || (type1 == VAR_FUNC && type2 == VAR_PARTIAL) - || (type1 == VAR_PARTIAL && type2 == VAR_FUNC)) + else if (vartype1 == VAR_ANY || vartype2 == VAR_ANY + || ((vartype1 == VAR_NUMBER || vartype1 == VAR_FLOAT) + && (vartype2 == VAR_NUMBER || vartype2 == VAR_FLOAT)) + || (vartype1 == VAR_FUNC && vartype2 == VAR_PARTIAL) + || (vartype1 == VAR_PARTIAL && vartype2 == VAR_FUNC)) isntype = ISN_COMPAREANY; - else if (type1 == VAR_SPECIAL || type2 == VAR_SPECIAL) + else if (vartype1 == VAR_SPECIAL || vartype2 == VAR_SPECIAL) { - switch (type1 == VAR_SPECIAL ? type2 : type1) + if ((vartype1 == VAR_SPECIAL + && (tv1 != NULL ? tv1->vval.v_number == VVAL_NONE + : type1 == &t_none) + && vartype2 != VAR_STRING) + || (vartype2 == VAR_SPECIAL + && (tv2 != NULL ? tv2->vval.v_number == VVAL_NONE + : type2 == &t_none) + && vartype1 != VAR_STRING)) + { + semsg(_(e_cannot_compare_str_with_str), + vartype_name(vartype1), vartype_name(vartype2)); + return ISN_DROP; + } + switch (vartype1 == VAR_SPECIAL ? vartype2 : vartype1) { case VAR_BLOB: break; case VAR_CHANNEL: break; @@ -387,7 +408,7 @@ get_compare_isn(exprtype_T exprtype, var case VAR_PARTIAL: break; case VAR_STRING: break; default: semsg(_(e_cannot_compare_str_with_str), - vartype_name(type1), vartype_name(type2)); + vartype_name(vartype1), vartype_name(vartype2)); return ISN_DROP; } isntype = ISN_COMPARENULL; @@ -400,20 +421,20 @@ get_compare_isn(exprtype_T exprtype, var || isntype == ISN_COMPAREFLOAT)) { semsg(_(e_cannot_use_str_with_str), - exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(type1)); + exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(vartype1)); return ISN_DROP; } if (isntype == ISN_DROP || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL - && (type1 == VAR_BOOL || type1 == VAR_SPECIAL - || type2 == VAR_BOOL || type2 == VAR_SPECIAL))) + && (vartype1 == VAR_BOOL || vartype1 == VAR_SPECIAL + || vartype2 == VAR_BOOL || vartype2 == 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)))) + && (vartype1 == VAR_BLOB || vartype2 == VAR_BLOB + || vartype1 == VAR_LIST || vartype2 == VAR_LIST)))) { semsg(_(e_cannot_compare_str_with_str), - vartype_name(type1), vartype_name(type2)); + vartype_name(vartype1), vartype_name(vartype2)); return ISN_DROP; } return isntype; @@ -422,7 +443,7 @@ get_compare_isn(exprtype_T exprtype, var int check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2) { - if (get_compare_isn(type, tv1->v_type, tv2->v_type) == ISN_DROP) + if (get_compare_isn(type, tv1, tv2, NULL, NULL) == ISN_DROP) return FAIL; return OK; } @@ -436,17 +457,14 @@ generate_COMPARE(cctx_T *cctx, exprtype_ isntype_T isntype; isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; - vartype_T type1; - vartype_T type2; RETURN_OK_IF_SKIP(cctx); // Get the known type of the two items on the stack. If they are matching // use a type-specific instruction. Otherwise fall back to runtime type // checking. - type1 = get_type_on_stack(cctx, 1)->tt_type; - type2 = get_type_on_stack(cctx, 0)->tt_type; - isntype = get_compare_isn(exprtype, type1, type2); + isntype = get_compare_isn(exprtype, NULL, NULL, + get_type_on_stack(cctx, 1), get_type_on_stack(cctx, 0)); if (isntype == ISN_DROP) return FAIL; @@ -664,7 +682,8 @@ generate_PUSHSPEC(cctx_T *cctx, varnumbe isn_T *isn; RETURN_OK_IF_SKIP(cctx); - if ((isn = generate_instr_type(cctx, ISN_PUSHSPEC, &t_special)) == NULL) + if ((isn = generate_instr_type(cctx, ISN_PUSHSPEC, + number == VVAL_NULL ? &t_null : &t_none)) == NULL) return FAIL; isn->isn_arg.number = number; @@ -1475,7 +1494,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufu type_T *actual; actual = get_type_on_stack(cctx, argcount - i - 1); - if (actual == &t_special + if (actual->tt_type == VAR_SPECIAL && i >= regular_args - ufunc->uf_def_args.ga_len) { // assume v:none used for default argument value @@ -1606,7 +1625,7 @@ generate_PCALL( expected = type->tt_args[ type->tt_argcount - 1]->tt_member; else if (i >= type->tt_min_argcount - && actual == &t_special) + && actual->tt_type == VAR_SPECIAL) expected = &t_any; else expected = type->tt_args[i];