To: vim_dev@googlegroups.com Subject: Patch 8.2.4539 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4539 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) Files: src/vim9instr.c, src/vim9compile.c, src/globals.h, src/testdir/test_vim9_expr.vim *** ../vim-8.2.4538/src/vim9instr.c 2022-03-08 19:43:51.688198945 +0000 --- src/vim9instr.c 2022-03-10 19:12:38.710964636 +0000 *************** *** 339,360 **** } /* ! * Get the instruction to use for comparing "type1" with "type2" * Return ISN_DROP when failed. */ static isntype_T ! get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2) { isntype_T isntype = ISN_DROP; ! if (type1 == VAR_UNKNOWN) ! type1 = VAR_ANY; ! if (type2 == VAR_UNKNOWN) ! type2 = VAR_ANY; ! if (type1 == type2) { ! switch (type1) { case VAR_BOOL: isntype = ISN_COMPAREBOOL; break; case VAR_SPECIAL: isntype = ISN_COMPARESPECIAL; break; --- 339,368 ---- } /* ! * 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, ! 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 (vartype1 == VAR_UNKNOWN) ! vartype1 = VAR_ANY; ! if (vartype2 == VAR_UNKNOWN) ! vartype2 = VAR_ANY; ! if (vartype1 == vartype2) { ! switch (vartype1) { case VAR_BOOL: isntype = ISN_COMPAREBOOL; break; case VAR_SPECIAL: isntype = ISN_COMPARESPECIAL; break; *************** *** 368,382 **** 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)) 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; --- 376,403 ---- default: isntype = ISN_COMPAREANY; break; } } ! 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 (vartype1 == VAR_SPECIAL || vartype2 == VAR_SPECIAL) { ! 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,393 **** 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; --- 408,414 ---- case VAR_PARTIAL: break; case VAR_STRING: break; default: semsg(_(e_cannot_compare_str_with_str), ! vartype_name(vartype1), vartype_name(vartype2)); return ISN_DROP; } isntype = ISN_COMPARENULL; *************** *** 400,419 **** || isntype == ISN_COMPAREFLOAT)) { semsg(_(e_cannot_use_str_with_str), ! exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(type1)); 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))) || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL && exprtype != EXPR_IS && exprtype != EXPR_ISNOT ! && (type1 == VAR_BLOB || type2 == VAR_BLOB ! || type1 == VAR_LIST || type2 == VAR_LIST)))) { semsg(_(e_cannot_compare_str_with_str), ! vartype_name(type1), vartype_name(type2)); return ISN_DROP; } return isntype; --- 421,440 ---- || isntype == ISN_COMPAREFLOAT)) { semsg(_(e_cannot_use_str_with_str), ! exprtype == EXPR_IS ? "is" : "isnot" , vartype_name(vartype1)); return ISN_DROP; } if (isntype == ISN_DROP || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL ! && (vartype1 == VAR_BOOL || vartype1 == VAR_SPECIAL ! || vartype2 == VAR_BOOL || vartype2 == VAR_SPECIAL))) || ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL && exprtype != EXPR_IS && exprtype != EXPR_ISNOT ! && (vartype1 == VAR_BLOB || vartype2 == VAR_BLOB ! || vartype1 == VAR_LIST || vartype2 == VAR_LIST)))) { semsg(_(e_cannot_compare_str_with_str), ! vartype_name(vartype1), vartype_name(vartype2)); return ISN_DROP; } return isntype; *************** *** 422,428 **** 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) return FAIL; return OK; } --- 443,449 ---- int check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2) { ! if (get_compare_isn(type, tv1, tv2, NULL, NULL) == ISN_DROP) return FAIL; return OK; } *************** *** 436,452 **** 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); if (isntype == ISN_DROP) return FAIL; --- 457,470 ---- isntype_T isntype; isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; 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. ! 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,670 **** isn_T *isn; RETURN_OK_IF_SKIP(cctx); ! if ((isn = generate_instr_type(cctx, ISN_PUSHSPEC, &t_special)) == NULL) return FAIL; isn->isn_arg.number = number; --- 682,689 ---- isn_T *isn; RETURN_OK_IF_SKIP(cctx); ! if ((isn = generate_instr_type(cctx, ISN_PUSHSPEC, ! number == VVAL_NULL ? &t_null : &t_none)) == NULL) return FAIL; isn->isn_arg.number = number; *************** *** 1475,1481 **** type_T *actual; actual = get_type_on_stack(cctx, argcount - i - 1); ! if (actual == &t_special && i >= regular_args - ufunc->uf_def_args.ga_len) { // assume v:none used for default argument value --- 1494,1500 ---- type_T *actual; actual = get_type_on_stack(cctx, argcount - i - 1); ! if (actual->tt_type == VAR_SPECIAL && i >= regular_args - ufunc->uf_def_args.ga_len) { // assume v:none used for default argument value *************** *** 1606,1612 **** expected = type->tt_args[ type->tt_argcount - 1]->tt_member; else if (i >= type->tt_min_argcount ! && actual == &t_special) expected = &t_any; else expected = type->tt_args[i]; --- 1625,1631 ---- expected = type->tt_args[ type->tt_argcount - 1]->tt_member; else if (i >= type->tt_min_argcount ! && actual->tt_type == VAR_SPECIAL) expected = &t_any; else expected = type->tt_args[i]; *** ../vim-8.2.4538/src/vim9compile.c 2022-03-08 13:18:10.809020782 +0000 --- src/vim9compile.c 2022-03-10 19:06:02.375695990 +0000 *************** *** 1799,1805 **** return FAIL; } type = get_type_on_stack(cctx, 0); ! if ((dest_type != VAR_BLOB && type != &t_special) && need_type(type, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; --- 1799,1805 ---- return FAIL; } type = get_type_on_stack(cctx, 0); ! if ((dest_type != VAR_BLOB && type->tt_type != VAR_SPECIAL) && need_type(type, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; *** ../vim-8.2.4538/src/globals.h 2022-02-11 20:33:11.942342190 +0000 --- src/globals.h 2022-03-10 19:06:20.151664158 +0000 *************** *** 398,404 **** 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_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); --- 398,405 ---- 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_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); *** ../vim-8.2.4538/src/testdir/test_vim9_expr.vim 2022-03-10 12:20:48.542552970 +0000 --- src/testdir/test_vim9_expr.vim 2022-03-10 19:21:06.485989124 +0000 *************** *** 828,835 **** 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() --- 828,849 ---- 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') + 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() *** ../vim-8.2.4538/src/version.c 2022-03-10 18:36:50.874611102 +0000 --- src/version.c 2022-03-10 19:21:40.625922704 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4539, /**/ -- hundred-and-one symptoms of being an internet addict: 225. You sign up for free subscriptions for all the computer magazines /// 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 ///