To: vim_dev@googlegroups.com Subject: Patch 8.2.4526 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4526 Problem: Vim9: cannot set variables to a null value. Solution: Add null_list, null_job, etc. Files: runtime/doc/vim9.txt, src/eval.c, src/proto/eval.pro, src/vim9expr.c, src/vim9script.c, src/vim9instr.c, src/vim9compile.c, src/vim9execute.c, src/vim9.h, src/vim9type.c, src/evalvars.c, src/testdir/test_vim9_assign.vim, src/testdir/test_vim9_disassemble.vim, src/testdir/test_vim9_func.vim, src/testdir/test_expr.vim *** ../vim-8.2.4525/runtime/doc/vim9.txt 2022-02-22 20:42:50.382992530 +0000 --- runtime/doc/vim9.txt 2022-03-07 21:26:34.420871466 +0000 *************** *** 94,101 **** def CallMe(count: number, message: string): bool - Call functions without `:call`: > writefile(['done'], 'file.txt') ! - You cannot use old Ex commands `:xit`, `:t`, `:k`, `:append`, `:change`, ! `:insert`, `:open`, and `:s` or `:d` with only flags. - You cannot use curly-braces names. - A range before a command must be prefixed with a colon: > :%s/this/that --- 94,113 ---- def CallMe(count: number, message: string): bool - Call functions without `:call`: > writefile(['done'], 'file.txt') ! - You cannot use old Ex commands: ! `:Print` ! `:append` ! `:change` ! `:d` directly followed by 'd' or 'p'. ! `:insert` ! `:k` ! `:mode` ! `:open` ! `:s` with only flags ! `:t` ! `:xit` ! - Some commands, especially those used for flow control, cannot be shortened. ! E.g., `:throw` cannot be written as `:th`. *E839* - You cannot use curly-braces names. - A range before a command must be prefixed with a colon: > :%s/this/that *************** *** 923,933 **** Simple types are Number, Float, Special and Bool. For other types |string()| should be used. ! *false* *true* *null* ! In Vim9 script one can use "true" for v:true, "false" for v:false and "null" ! for v:null. When converting a boolean to a string "false" and "true" are ! used, not "v:false" and "v:true" like in legacy script. "v:none" is not ! changed, it is only used in JSON and has no equivalent in other languages. Indexing a string with [idx] or taking a slice with [idx : idx] uses character indexes instead of byte indexes. Composing characters are included. --- 966,1003 ---- Simple types are Number, Float, Special and Bool. For other types |string()| should be used. ! *false* *true* *null* *E1034* ! In Vim9 script one can use the following predefined values: > ! true ! false ! null ! null_blob ! null_channel ! null_dict ! null_function ! null_job ! null_list ! null_partial ! null_string ! `true` is the same as `v:true`, `false` the same as `v:false`, `null` the same ! as `v:null`. ! ! While `null` has the type "special", the other "null_" types have the type ! indicated by their name. Quite often a null value is handled the same as an ! empty value, but not always. The values can be useful to clear a script-local ! variable, since they cannot be deleted with `:unlet`. E.g.: > ! var theJob = job_start(...) ! # let the job do its work ! theJob = null_job ! ! The values can also be useful as the default value for an argument: > ! def MyFunc(b: blob = null_blob) ! if b == null_blob ! # b argument was not given ! ! When converting a boolean to a string `false` and `true` are used, not ! `v:false` and `v:true` like in legacy script. `v:none` has no `none` ! replacement, it has no equivalent in other languages. Indexing a string with [idx] or taking a slice with [idx : idx] uses character indexes instead of byte indexes. Composing characters are included. *** ../vim-8.2.4525/src/eval.c 2022-02-13 21:51:02.392484124 +0000 --- src/eval.c 2022-03-07 21:40:00.798772322 +0000 *************** *** 943,948 **** --- 943,949 ---- type_list = &SCRIPT_ITEM(current_sctx.sc_sid)->sn_type_list; else { + // TODO: should we give an error here? type_list = &tmp_type_list; ga_init2(type_list, sizeof(type_T), 10); } *************** *** 3483,3488 **** --- 3484,3583 ---- } /* + * Check for a predefined value "true", "false" and "null.*". + * Return OK when recognized. + */ + int + handle_predefined(char_u *s, int len, typval_T *rettv) + { + switch (len) + { + case 4: if (STRNCMP(s, "true", 4) == 0) + { + rettv->v_type = VAR_BOOL; + rettv->vval.v_number = VVAL_TRUE; + return OK; + } + if (STRNCMP(s, "null", 4) == 0) + { + rettv->v_type = VAR_SPECIAL; + rettv->vval.v_number = VVAL_NULL; + return OK; + } + break; + case 5: if (STRNCMP(s, "false", 5) == 0) + { + rettv->v_type = VAR_BOOL; + rettv->vval.v_number = VVAL_FALSE; + return OK; + } + break; + #ifdef FEAT_JOB_CHANNEL + case 8: if (STRNCMP(s, "null_job", 8) == 0) + { + rettv->v_type = VAR_JOB; + rettv->vval.v_job = NULL; + return OK; + } + break; + #endif + case 9: + if (STRNCMP(s, "null_", 5) != 0) + break; + if (STRNCMP(s + 5, "list", 4) == 0) + { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = NULL; + return OK; + } + if (STRNCMP(s + 5, "dict", 4) == 0) + { + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = NULL; + return OK; + } + if (STRNCMP(s + 5, "blob", 4) == 0) + { + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; + return OK; + } + break; + case 11: if (STRNCMP(s, "null_string", 11) == 0) + { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + return OK; + } + break; + case 12: + #ifdef FEAT_JOB_CHANNEL + if (STRNCMP(s, "null_channel", 12) == 0) + { + rettv->v_type = VAR_CHANNEL; + rettv->vval.v_channel = NULL; + return OK; + } + #endif + if (STRNCMP(s, "null_partial", 12) == 0) + { + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = NULL; + return OK; + } + break; + case 13: if (STRNCMP(s, "null_function", 13) == 0) + { + rettv->v_type = VAR_FUNC; + rettv->vval.v_string = NULL; + return OK; + } + break; + } + return FAIL; + } + + /* * Handle sixth level expression: * number number constant * 0zFFFFFFFF Blob constant *************** *** 3757,3782 **** ret = FAIL; else if (evaluate) { ! // get the value of "true", "false" or a variable ! if (len == 4 && vim9script && STRNCMP(s, "true", 4) == 0) ! { ! rettv->v_type = VAR_BOOL; ! rettv->vval.v_number = VVAL_TRUE; ! ret = OK; ! } ! else if (len == 5 && vim9script && STRNCMP(s, "false", 5) == 0) ! { ! rettv->v_type = VAR_BOOL; ! rettv->vval.v_number = VVAL_FALSE; ! ret = OK; ! } ! else if (len == 4 && vim9script && STRNCMP(s, "null", 4) == 0) ! { ! rettv->v_type = VAR_SPECIAL; ! rettv->vval.v_number = VVAL_NULL; ! ret = OK; ! } ! else { name_start = s; ret = eval_variable(s, len, 0, rettv, NULL, --- 3852,3862 ---- ret = FAIL; else if (evaluate) { ! // get the value of "true", "false", etc. or a variable ! ret = FAIL; ! if (vim9script) ! ret = handle_predefined(s, len, rettv); ! if (ret == FAIL) { name_start = s; ret = eval_variable(s, len, 0, rettv, NULL, *** ../vim-8.2.4525/src/proto/eval.pro 2022-02-02 20:01:21.957210955 +0000 --- src/proto/eval.pro 2022-03-07 21:40:03.670777363 +0000 *************** *** 42,47 **** --- 42,48 ---- void eval_addblob(typval_T *tv1, typval_T *tv2); int eval_addlist(typval_T *tv1, typval_T *tv2); int eval_leader(char_u **arg, int vim9); + int handle_predefined(char_u *s, int len, typval_T *rettv); int check_can_index(typval_T *rettv, int evaluate, int verbose); void f_slice(typval_T *argvars, typval_T *rettv); int eval_index_inner(typval_T *rettv, int is_range, typval_T *var1, typval_T *var2, int exclusive, char_u *key, int keylen, int verbose); *** ../vim-8.2.4525/src/vim9expr.c 2022-02-22 20:42:50.382992530 +0000 --- src/vim9expr.c 2022-03-07 21:40:10.746789629 +0000 *************** *** 2107,2120 **** break; /* ! * "null" constant */ ! case 'n': if (STRNCMP(*arg, "null", 4) == 0 ! && !eval_isnamec((*arg)[4])) { ! *arg += 4; ! rettv->v_type = VAR_SPECIAL; ! rettv->vval.v_number = VVAL_NULL; } else ret = NOTDONE; --- 2107,2126 ---- break; /* ! * "null" or "null_*" constant */ ! case 'n': if (STRNCMP(*arg, "null", 4) == 0) { ! char_u *p = *arg + 4; ! int len; ! ! for (len = 0; eval_isnamec(p[len]); ++len) ! ; ! ret = handle_predefined(*arg, len + 4, rettv); ! if (ret == FAIL) ! ret = NOTDONE; ! else ! *arg += len + 4; } else ret = NOTDONE; *** ../vim-8.2.4525/src/vim9script.c 2022-02-13 21:51:02.392484124 +0000 --- src/vim9script.c 2022-03-07 21:53:02.483290072 +0000 *************** *** 1062,1067 **** --- 1062,1075 ---- "true", "false", "null", + "null_blob", + "null_dict", + "null_function", + "null_list", + "null_partial", + "null_string", + "null_channel", + "null_job", "this", NULL }; *** ../vim-8.2.4525/src/vim9instr.c 2022-03-01 19:23:20.540357322 +0000 --- src/vim9instr.c 2022-03-08 11:10:13.799498306 +0000 *************** *** 570,575 **** --- 570,609 ---- generate_PUSHBLOB(cctx, tv->vval.v_blob); tv->vval.v_blob = NULL; break; + case VAR_LIST: + if (tv->vval.v_list != NULL) + iemsg("non-empty list constant not supported"); + generate_NEWLIST(cctx, 0); + break; + case VAR_DICT: + if (tv->vval.v_dict != NULL) + iemsg("non-empty dict constant not supported"); + generate_NEWDICT(cctx, 0); + break; + #ifdef FEAT_JOB_CHANNEL + case VAR_JOB: + if (tv->vval.v_job != NULL) + iemsg("non-null job constant not supported"); + generate_PUSHJOB(cctx, NULL); + break; + case VAR_CHANNEL: + if (tv->vval.v_channel != NULL) + iemsg("non-null channel constant not supported"); + generate_PUSHCHANNEL(cctx, NULL); + break; + #endif + case VAR_FUNC: + if (tv->vval.v_string != NULL) + iemsg("non-null function constant not supported"); + generate_PUSHFUNC(cctx, NULL, &t_func_unknown); + break; + case VAR_PARTIAL: + if (tv->vval.v_partial != NULL) + iemsg("non-null partial constant not supported"); + if (generate_instr_type(cctx, ISN_NEWPARTIAL, &t_func_unknown) + == NULL) + return FAIL; + break; case VAR_STRING: generate_PUSHS(cctx, &tv->vval.v_string); tv->vval.v_string = NULL; *************** *** 706,712 **** isn_T *isn; RETURN_OK_IF_SKIP(cctx); ! if ((isn = generate_instr_type(cctx, ISN_PUSHJOB, &t_channel)) == NULL) return FAIL; isn->isn_arg.job = job; --- 740,746 ---- isn_T *isn; RETURN_OK_IF_SKIP(cctx); ! if ((isn = generate_instr_type(cctx, ISN_PUSHJOB, &t_job)) == NULL) return FAIL; isn->isn_arg.job = job; *************** *** 2185,2190 **** --- 2219,2225 ---- case ISN_NEGATENR: case ISN_NEWDICT: case ISN_NEWLIST: + case ISN_NEWPARTIAL: case ISN_OPANY: case ISN_OPFLOAT: case ISN_OPNR: *** ../vim-8.2.4525/src/vim9compile.c 2022-03-05 12:56:39.912475510 +0000 --- src/vim9compile.c 2022-03-08 11:01:04.560170373 +0000 *************** *** 403,410 **** if (ret == OK) return OK; // If the actual type can be the expected type add a runtime check. - // If it's a constant a runtime check makes no sense. if (!actual_is_const && ret == MAYBE && use_typecheck(actual, expected)) { generate_TYPECHECK(cctx, expected, offset, where.wt_index); --- 403,414 ---- if (ret == OK) return OK; + // If actual a constant a runtime check makes no sense. If it's + // null_function it is OK. + if (actual_is_const && ret == MAYBE && actual == &t_func_unknown) + return OK; + // If the actual type can be the expected type add a runtime check. if (!actual_is_const && ret == MAYBE && use_typecheck(actual, expected)) { generate_TYPECHECK(cctx, expected, offset, where.wt_index); *** ../vim-8.2.4525/src/vim9execute.c 2022-03-01 19:23:20.544357315 +0000 --- src/vim9execute.c 2022-03-07 22:10:11.223852520 +0000 *************** *** 3440,3445 **** --- 3440,3456 ---- } break; + // create a partial with NULL value + case ISN_NEWPARTIAL: + if (GA_GROW_FAILS(&ectx->ec_stack, 1)) + goto theend; + ++ectx->ec_stack.ga_len; + tv = STACK_TV_BOT(-1); + tv->v_type = VAR_PARTIAL; + tv->v_lock = 0; + tv->vval.v_partial = NULL; + break; + // call a :def function case ISN_DCALL: SOURCING_LNUM = iptr->isn_lnum; *************** *** 5720,5725 **** --- 5731,5739 ---- smsg("%s%4d NEWDICT size %lld", pfx, current, (varnumber_T)(iptr->isn_arg.number)); break; + case ISN_NEWPARTIAL: + smsg("%s%4d NEWPARTIAL", pfx, current); + break; // function call case ISN_BCALL: *** ../vim-8.2.4525/src/vim9.h 2022-03-01 19:23:20.544357315 +0000 --- src/vim9.h 2022-03-07 22:12:05.675987992 +0000 *************** *** 91,96 **** --- 91,97 ---- ISN_PUSHJOB, // push channel isn_arg.job ISN_NEWLIST, // push list from stack items, size is isn_arg.number ISN_NEWDICT, // push dict from stack items, size is isn_arg.number + ISN_NEWPARTIAL, // push NULL partial ISN_AUTOLOAD, // get item from autoload import, function or variable *** ../vim-8.2.4525/src/vim9type.c 2022-02-21 13:13:44.923693160 +0000 --- src/vim9type.c 2022-03-08 12:25:45.602214326 +0000 *************** *** 567,588 **** if (expected == NULL) return OK; // didn't expect anything. ! // For some values there is no type, assume an error will be given later ! // for an invalid value. if ((actual_tv->v_type == VAR_FUNC && actual_tv->vval.v_string == NULL) || (actual_tv->v_type == VAR_PARTIAL && actual_tv->vval.v_partial == NULL)) ! { ! emsg(_(e_function_reference_is_not_set)); ! return FAIL; ! } ! ! ga_init2(&type_list, sizeof(type_T *), 10); ! ! // When the actual type is list or dict go through the values to ! // possibly get a more specific type. ! actual_type = typval2type(actual_tv, get_copyID(), &type_list, TVTT_DO_MEMBER | TVTT_MORE_SPECIFIC); if (actual_type != NULL) { --- 567,585 ---- if (expected == NULL) return OK; // didn't expect anything. + // + ga_init2(&type_list, sizeof(type_T *), 10); ! // A null_function and null_partial are special cases, they can be used to ! // clear a variable. if ((actual_tv->v_type == VAR_FUNC && actual_tv->vval.v_string == NULL) || (actual_tv->v_type == VAR_PARTIAL && actual_tv->vval.v_partial == NULL)) ! actual_type = &t_func_unknown; ! else ! // When the actual type is list or dict go through the values ! // to possibly get a more specific type. ! actual_type = typval2type(actual_tv, get_copyID(), &type_list, TVTT_DO_MEMBER | TVTT_MORE_SPECIFIC); if (actual_type != NULL) { *** ../vim-8.2.4525/src/evalvars.c 2022-03-05 12:56:39.912475510 +0000 --- src/evalvars.c 2022-03-08 12:35:36.801291216 +0000 *************** *** 999,1004 **** --- 999,1009 ---- listitem_T *item; typval_T ltv; + if (tv->v_type == VAR_VOID) + { + emsg(_(e_cannot_use_void_value)); + return FAIL; + } if (*arg != '[') { // ":let var = expr" or ":for var in list" *** ../vim-8.2.4525/src/testdir/test_vim9_assign.vim 2022-03-05 11:37:43.048924316 +0000 --- src/testdir/test_vim9_assign.vim 2022-03-07 22:12:34.436017099 +0000 *************** *** 306,317 **** enddef def Test_reserved_name() ! for name in ['true', 'false', 'null'] v9.CheckDefExecAndScriptFailure(['var ' .. name .. ' = 0'], 'E1034:') v9.CheckDefExecAndScriptFailure(['var ' .. name .. ': bool'], 'E1034:') endfor enddef def Test_skipped_assignment() var lines =<< trim END for x in [] --- 306,349 ---- enddef def Test_reserved_name() ! var more_names = ['null_job', 'null_channel'] ! if !has('job') ! more_names = [] ! endif ! ! for name in ['true', ! 'false', ! 'null', ! 'null_blob', ! 'null_dict', ! 'null_function', ! 'null_list', ! 'null_partial', ! 'null_string', ! ] + more_names v9.CheckDefExecAndScriptFailure(['var ' .. name .. ' = 0'], 'E1034:') v9.CheckDefExecAndScriptFailure(['var ' .. name .. ': bool'], 'E1034:') endfor enddef + def Test_null_values() + var lines =<< trim END + var b: blob = null_blob + var dn: dict = null_dict + var ds: dict = null_dict + var ln: list = null_list + var ls: list = null_list + var Ff: func(string): string = null_function + var Fp: func(number): number = null_partial + var s: string = null_string + if has('job') + var j: job = null_job + var c: channel = null_channel + endif + END + v9.CheckDefAndScriptSuccess(lines) + enddef + def Test_skipped_assignment() var lines =<< trim END for x in [] *** ../vim-8.2.4525/src/testdir/test_vim9_disassemble.vim 2022-03-01 19:23:20.544357315 +0000 --- src/testdir/test_vim9_disassemble.vim 2022-03-08 11:24:09.458948621 +0000 *************** *** 415,420 **** --- 415,472 ---- res) enddef + if has('job') + def s:StoreNull() + var ss = null_string + var bb = null_blob + var dd = null_dict + var ll = null_list + var Ff = null_function + var Pp = null_partial + var jj = null_job + var cc = null_channel + enddef + + def Test_disassemble_assign_null() + var res = execute('disass s:StoreNull') + assert_match('\d*_StoreNull\_s*' .. + 'var ss = null_string\_s*' .. + '\d\+ PUSHS "\[NULL\]"\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var bb = null_blob\_s*' .. + '\d\+ PUSHBLOB 0z\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var dd = null_dict\_s*' .. + '\d\+ NEWDICT size 0\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var ll = null_list\_s*' .. + '\d\+ NEWLIST size 0\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var Ff = null_function\_s*' .. + '\d\+ PUSHFUNC "\[none\]"\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var Pp = null_partial\_s*' .. + '\d\+ NEWPARTIAL\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var jj = null_job\_s*' .. + '\d\+ PUSHJOB "no process"\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var cc = null_channel\_s*' .. + '\d\+ PUSHCHANNEL 0\_s*' .. + '\d\+ STORE $\d\_s*' .. + + '\d\+ RETURN void', + res) + enddef + endif + def s:ScriptFuncStoreIndex() var d = {dd: {}} d.dd[0] = 0 *** ../vim-8.2.4525/src/testdir/test_vim9_func.vim 2022-02-28 20:54:58.129239044 +0000 --- src/testdir/test_vim9_func.vim 2022-03-08 12:36:14.337235053 +0000 *************** *** 3326,3332 **** var Expr: func(dict): dict const Call = Foo(Expr) END ! v9.CheckScriptFailure(lines, 'E1235:') enddef def Test_partial_double_nested() --- 3326,3332 ---- var Expr: func(dict): dict const Call = Foo(Expr) END ! v9.CheckScriptFailure(lines, 'E1031:') enddef def Test_partial_double_nested() *** ../vim-8.2.4525/src/testdir/test_expr.vim 2022-02-26 11:46:09.510212631 +0000 --- src/testdir/test_expr.vim 2022-03-08 13:12:08.461599684 +0000 *************** *** 157,168 **** func Test_loop_over_null_list() let lines =<< trim END ! VAR null_list = test_null_list() ! for i in null_list call assert_report('should not get here') endfor END call v9.CheckLegacyAndVim9Success(lines) endfunc func Test_setreg_null_list() --- 157,184 ---- func Test_loop_over_null_list() let lines =<< trim END ! VAR nulllist = test_null_list() ! for i in nulllist call assert_report('should not get here') endfor END call v9.CheckLegacyAndVim9Success(lines) + + let lines =<< trim END + var nulllist = null_list + for i in nulllist + call assert_report('should not get here') + endfor + END + call v9.CheckDefAndScriptSuccess(lines) + + let lines =<< trim END + let nulllist = null_list + for i in nulllist + call assert_report('should not get here') + endfor + END + call v9.CheckScriptFailure(lines, 'E121: Undefined variable: null_list') endfunc func Test_setreg_null_list() *** ../vim-8.2.4525/src/version.c 2022-03-07 16:57:18.335101482 +0000 --- src/version.c 2022-03-08 12:26:16.074164231 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4526, /**/ -- Some of the well known MS-Windows errors: ETIME Wrong time, wait a little while ECRASH Try again... EDETECT Unable to detect errors EOVER You lost! Play another game? ENOCLUE Eh, what did you want? /// 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 ///