From 767034c5b82ba8999d9fed2f997436e6e3e99419 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 9 Apr 2021 17:24:52 +0200 Subject: [PATCH 01/29] patch 8.2.2739: Vim9: a lambda accepts too many arguments at the script level Problem: Vim9: a lambda accepts too many arguments at the script level. Solution: Do not set uf_varargs in Vim9 script. --- src/testdir/test_vim9_func.vim | 9 +++++++-- src/testdir/test_vim9_script.vim | 2 +- src/userfunc.c | 5 +++-- src/version.c | 2 ++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 730cf40aaa..b0669fe651 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -836,6 +836,11 @@ def Test_call_lambda_args() enddef END CheckDefFailure(lines, 'E1167:') + + lines =<< trim END + echo ((a) => a)('aa', 'bb') + END + CheckDefAndScriptFailure(lines, 'E118:', 1) enddef def FilterWithCond(x: string, Cond: func(string): bool): bool @@ -2114,7 +2119,7 @@ def Test_list_lambda() ->substitute("('", ' ', '') ->substitute("')", '', '') ->substitute('function\zs', ' ', '')) - assert_match('def \d\+(_: any, ...): number\n1 return 0\n enddef', body) + assert_match('def \d\+(_: any): number\n1 return 0\n enddef', body) enddef def DoFilterThis(a: string): list @@ -2363,7 +2368,7 @@ def Test_did_emsg_reset() vim9script au BufWinLeave * # def Func() - popup_menu('', {callback: () => popup_create('', {})->popup_close()}) + popup_menu('', {callback: (a, b) => popup_create('', {})->popup_close()}) eval [][0] enddef nno call Func() diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index cb1592abff..98f40bb8f9 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -3518,7 +3518,7 @@ func Test_no_redraw_when_restoring_cpo() vim9script set cpo+=M exe 'set rtp^=' .. getcwd() .. '/Xdir' - au CmdlineEnter : ++once timer_start(0, () => script#func()) + au CmdlineEnter : ++once timer_start(0, (_) => script#func()) setline(1, 'some text') END call writefile(lines, 'XTest_redraw_cpo') diff --git a/src/userfunc.c b/src/userfunc.c index af107b0db6..81a73e2803 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1263,8 +1263,9 @@ get_lambda_tv( #endif if (sandbox) flags |= FC_SANDBOX; - // can be called with more args than uf_args.ga_len - fp->uf_varargs = TRUE; + // In legacy script a lambda can be called with more args than + // uf_args.ga_len. + fp->uf_varargs = !in_vim9script(); fp->uf_flags = flags; fp->uf_calls = 0; fp->uf_script_ctx = current_sctx; diff --git a/src/version.c b/src/version.c index 452565c1f2..a2bd55869a 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2739, /**/ 2738, /**/ From 2a38908b05c1d3973a8edbeb5b3e05a11332faf0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 9 Apr 2021 20:24:31 +0200 Subject: [PATCH 02/29] patch 8.2.2740: Vim9: lambda with varargs doesn't work Problem: Vim9: lambda with varargs doesn't work. Solution: Make "...name" work. Require type to be a list. --- src/errors.h | 2 + src/testdir/test_vim9_func.vim | 21 +++++++++- src/testdir/test_vim9_script.vim | 6 +-- src/userfunc.c | 67 +++++++++++++++++++------------- src/version.c | 2 + src/vim9compile.c | 5 ++- src/vim9execute.c | 2 +- 7 files changed, 71 insertions(+), 34 deletions(-) diff --git a/src/errors.h b/src/errors.h index 0225531cb0..e4836bc4ac 100644 --- a/src/errors.h +++ b/src/errors.h @@ -395,3 +395,5 @@ EXTERN char e_cannot_lock_unlock_local_variable[] INIT(= N_("E1178: Cannot lock or unlock a local variable")); EXTERN char e_failed_to_extract_pwd_from_str_check_your_shell_config[] INIT(= N_("E1179: Failed to extract PWD from %s, check your shell's config related to OSC 7")); +EXTERN char e_variable_arguments_type_must_be_list_str[] + INIT(= N_("E1180: Variable arguments type must be a list: %s")); diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index b0669fe651..d48cab452e 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -791,10 +791,18 @@ def Test_call_funcref_wrong_args() enddef def Test_call_lambda_args() + var lines =<< trim END + var Callback = (..._) => 'anything' + assert_equal('anything', Callback()) + assert_equal('anything', Callback(1)) + assert_equal('anything', Callback('a', 2)) + END + CheckDefAndScriptSuccess(lines) + CheckDefFailure(['echo ((i) => 0)()'], 'E119: Not enough arguments for function: ((i) => 0)()') - var lines =<< trim END + lines =<< trim END var Ref = (x: number, y: number) => x + y echo Ref(1, 'x') END @@ -923,13 +931,22 @@ def Test_call_def_varargs() lines =<< trim END vim9script - def Func(...l: any) + def Func(...l: list) echo l enddef Func(0) END CheckScriptSuccess(lines) + lines =<< trim END + vim9script + def Func(...l: any) + echo l + enddef + Func(0) + END + CheckScriptFailure(lines, 'E1180:', 2) + lines =<< trim END vim9script def Func(..._l: list) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 98f40bb8f9..7155e8f3b0 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -3644,7 +3644,7 @@ enddef def Test_catch_exception_in_callback() var lines =<< trim END vim9script - def Callback(...l: any) + def Callback(...l: list) try var x: string var y: string @@ -3669,10 +3669,10 @@ def Test_no_unknown_error_after_error() var lines =<< trim END vim9script var source: list - def Out_cb(...l: any) + def Out_cb(...l: list) eval [][0] enddef - def Exit_cb(...l: any) + def Exit_cb(...l: list) sleep 1m source += l enddef diff --git a/src/userfunc.c b/src/userfunc.c index 81a73e2803..8d06b4916c 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -68,6 +68,7 @@ one_function_arg( garray_T *argtypes, int types_optional, evalarg_T *evalarg, + int is_vararg, int skip) { char_u *p = arg; @@ -155,7 +156,8 @@ one_function_arg( { if (type == NULL && types_optional) // lambda arguments default to "any" type - type = vim_strsave((char_u *)"any"); + type = vim_strsave((char_u *) + (is_vararg ? "list" : "any")); ((char_u **)argtypes->ga_data)[argtypes->ga_len++] = type; } } @@ -250,7 +252,7 @@ get_function_args( arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, - evalarg, skip); + evalarg, TRUE, skip); if (p == arg) break; if (*skipwhite(p) == '=') @@ -264,7 +266,7 @@ get_function_args( { arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, - evalarg, skip); + evalarg, FALSE, skip); if (p == arg) break; @@ -360,12 +362,14 @@ err_ret: static int parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs) { + int len = 0; + ga_init2(&fp->uf_type_list, sizeof(type_T *), 10); if (argtypes->ga_len > 0) { // When "varargs" is set the last name/type goes into uf_va_name // and uf_va_type. - int len = argtypes->ga_len - (varargs ? 1 : 0); + len = argtypes->ga_len - (varargs ? 1 : 0); if (len > 0) fp->uf_arg_types = ALLOC_CLEAR_MULT(type_T *, len); @@ -388,25 +392,35 @@ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs) fp->uf_arg_types[i] = type; } } - if (varargs) - { - char_u *p; - - // Move the last argument "...name: type" to uf_va_name and - // uf_va_type. - fp->uf_va_name = ((char_u **)fp->uf_args.ga_data) - [fp->uf_args.ga_len - 1]; - --fp->uf_args.ga_len; - p = ((char_u **)argtypes->ga_data)[len]; - if (p == NULL) - // todo: get type from default value - fp->uf_va_type = &t_any; - else - fp->uf_va_type = parse_type(&p, &fp->uf_type_list, TRUE); - if (fp->uf_va_type == NULL) - return FAIL; - } } + + if (varargs) + { + char_u *p; + + // Move the last argument "...name: type" to uf_va_name and + // uf_va_type. + fp->uf_va_name = ((char_u **)fp->uf_args.ga_data) + [fp->uf_args.ga_len - 1]; + --fp->uf_args.ga_len; + p = ((char_u **)argtypes->ga_data)[len]; + if (p == NULL) + // TODO: get type from default value + fp->uf_va_type = &t_list_any; + else + { + fp->uf_va_type = parse_type(&p, &fp->uf_type_list, TRUE); + if (fp->uf_va_type != NULL && fp->uf_va_type->tt_type != VAR_LIST) + { + semsg(_(e_variable_arguments_type_must_be_list_str), + ((char_u **)argtypes->ga_data)[len]); + return FAIL; + } + } + if (fp->uf_va_type == NULL) + return FAIL; + } + return OK; } @@ -1236,7 +1250,8 @@ get_lambda_tv( ga_init(&fp->uf_def_args); if (types_optional) { - if (parse_argument_types(fp, &argtypes, FALSE) == FAIL) + if (parse_argument_types(fp, &argtypes, + in_vim9script() && varargs) == FAIL) goto errret; if (ret_type != NULL) { @@ -1264,8 +1279,8 @@ get_lambda_tv( if (sandbox) flags |= FC_SANDBOX; // In legacy script a lambda can be called with more args than - // uf_args.ga_len. - fp->uf_varargs = !in_vim9script(); + // uf_args.ga_len. In Vim9 script "...name" has to be used. + fp->uf_varargs = !in_vim9script() || varargs; fp->uf_flags = flags; fp->uf_calls = 0; fp->uf_script_ctx = current_sctx; @@ -3190,7 +3205,7 @@ list_func_head(ufunc_T *fp, int indent) msg_puts(", "); msg_puts("..."); msg_puts((char *)fp->uf_va_name); - if (fp->uf_va_type) + if (fp->uf_va_type != NULL) { char *tofree; diff --git a/src/version.c b/src/version.c index a2bd55869a..74a0ae6e7f 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2740, /**/ 2739, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 00a8f56469..2cfc12798d 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1856,7 +1856,8 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount) continue; expected = ufunc->uf_arg_types[i]; } - else if (ufunc->uf_va_type == NULL || ufunc->uf_va_type == &t_any) + else if (ufunc->uf_va_type == NULL + || ufunc->uf_va_type == &t_list_any) // possibly a lambda or "...: any" expected = &t_any; else @@ -9069,7 +9070,7 @@ set_function_type(ufunc_T *ufunc) if (varargs) { ufunc->uf_func_type->tt_args[argcount] = - ufunc->uf_va_type == NULL ? &t_any : ufunc->uf_va_type; + ufunc->uf_va_type == NULL ? &t_list_any : ufunc->uf_va_type; ufunc->uf_func_type->tt_flags = TTFLAG_VARARGS; } } diff --git a/src/vim9execute.c b/src/vim9execute.c index d6c4764882..eddb90f9ff 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1374,7 +1374,7 @@ call_def_function( // Check the type of the list items. tv = STACK_TV_BOT(-1); if (ufunc->uf_va_type != NULL - && ufunc->uf_va_type != &t_any + && ufunc->uf_va_type != &t_list_any && ufunc->uf_va_type->tt_member != &t_any && tv->vval.v_list != NULL) { From 1088b69451c739c698cf4c2003c2b994458ad18b Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 9 Apr 2021 22:12:44 +0200 Subject: [PATCH 03/29] patch 8.2.2741: Vim9: Partial call does not check right arguments Problem: Vim9: Partial call does not check right arguments. Solution: Adjust the offset for whether the partial is before or after the arguments. (closes #8091) --- src/testdir/test_vim9_func.vim | 2 ++ src/version.c | 2 ++ src/vim9compile.c | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index d48cab452e..85830a36ce 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -796,6 +796,8 @@ def Test_call_lambda_args() assert_equal('anything', Callback()) assert_equal('anything', Callback(1)) assert_equal('anything', Callback('a', 2)) + + assert_equal('xyz', ((a: string): string => a)('xyz')) END CheckDefAndScriptSuccess(lines) diff --git a/src/version.c b/src/version.c index 74a0ae6e7f..64c5af40b2 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2741, /**/ 2740, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 2cfc12798d..c2735dcbd4 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1969,7 +1969,7 @@ generate_PCALL( for (i = 0; i < argcount; ++i) { - int offset = -argcount + i - 1; + int offset = -argcount + i - (at_top ? 0 : 1); type_T *actual = ((type_T **)stack->ga_data)[ stack->ga_len + offset]; type_T *expected; From 701cc6ca9e940665a9424541f989bb38c853a498 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 13:33:48 +0200 Subject: [PATCH 04/29] patch 8.2.2742: Vim9: when compiling a function fails it is cleared Problem: Vim9: when compiling a function fails it is cleared. Solution: Keep the function lines, prevent execution with a different status. (closes #8093) --- src/structs.h | 9 +++++---- src/testdir/test_vim9_func.vim | 16 +++++++++++++++- src/version.c | 2 ++ src/vim9compile.c | 19 +++++++++++-------- src/vim9execute.c | 1 + 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/structs.h b/src/structs.h index dd802152c2..b4bafa5a98 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1583,10 +1583,11 @@ typedef struct funccall_S funccall_T; // values used for "uf_def_status" typedef enum { - UF_NOT_COMPILED, - UF_TO_BE_COMPILED, - UF_COMPILING, - UF_COMPILED + UF_NOT_COMPILED, // executed with interpreter + UF_TO_BE_COMPILED, // to be compiled before execution + UF_COMPILING, // in compile_def_function() + UF_COMPILED, // successfully compiled + UF_COMPILE_ERROR // compilation error, cannot execute } def_status_T; /* diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 85830a36ce..6a3dfbbe6d 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -1498,7 +1498,7 @@ def Test_redef_failure() so Xdef delete('Xdef') - g:Func0()->assert_equal(0) + assert_fails('g:Func0()', 'E1091:') g:Func1()->assert_equal('Func1') g:Func2()->assert_equal('Func2') @@ -2591,6 +2591,20 @@ def Test_check_func_arg_types() CheckScriptFailure(lines + ['echo H(G(F2))'], 'E1013:') enddef +def Test_compile_error() + var lines =<< trim END + def g:Broken() + echo 'a' + {} + enddef + call g:Broken() + END + # First call: compilation error + CheckScriptFailure(lines, 'E1051: Wrong argument type for +') + + # Second call won't try compiling again + assert_fails('call g:Broken()', 'E1091: Function is not compiled: Broken') +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 64c5af40b2..5cf56feadc 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2742, /**/ 2741, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index c2735dcbd4..aa5894a355 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1791,8 +1791,9 @@ func_needs_compiling(ufunc_T *ufunc, int profile UNUSED) { switch (ufunc->uf_def_status) { - case UF_NOT_COMPILED: break; - case UF_TO_BE_COMPILED: return TRUE; + case UF_TO_BE_COMPILED: + return TRUE; + case UF_COMPILED: { #ifdef FEAT_PROFILE @@ -1805,7 +1806,11 @@ func_needs_compiling(ufunc_T *ufunc, int profile UNUSED) break; #endif } - case UF_COMPILING: break; + + case UF_NOT_COMPILED: + case UF_COMPILE_ERROR: + case UF_COMPILING: + break; } return FALSE; } @@ -1834,7 +1839,8 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount) return FAIL; } - if (ufunc->uf_def_status != UF_NOT_COMPILED) + if (ufunc->uf_def_status != UF_NOT_COMPILED + && ufunc->uf_def_status != UF_COMPILE_ERROR) { int i; @@ -9007,14 +9013,11 @@ erret: --def_functions.ga_len; ufunc->uf_dfunc_idx = 0; } - ufunc->uf_def_status = UF_NOT_COMPILED; + ufunc->uf_def_status = UF_COMPILE_ERROR; while (cctx.ctx_scope != NULL) drop_scope(&cctx); - // Don't execute this function body. - ga_clear_strings(&ufunc->uf_lines); - if (errormsg != NULL) emsg(errormsg); else if (did_emsg == did_emsg_before) diff --git a/src/vim9execute.c b/src/vim9execute.c index eddb90f9ff..a49d305532 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1297,6 +1297,7 @@ call_def_function( #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_frame_idx + STACK_FRAME_SIZE + idx) if (ufunc->uf_def_status == UF_NOT_COMPILED + || ufunc->uf_def_status == UF_COMPILE_ERROR || (func_needs_compiling(ufunc, PROFILING(ufunc)) && compile_def_function(ufunc, FALSE, PROFILING(ufunc), NULL) == FAIL)) From 599410cb3cb19946cd6df22441da5de003e114bb Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 14:03:43 +0200 Subject: [PATCH 05/29] patch 8.2.2743: Vim9: function state stuck when compiling with ":silent!" Problem: Vim9: function state stuck when compiling with ":silent!". Solution: Check for uf_def_status to be UF_COMPILING. --- src/globals.h | 3 +++ src/message.c | 3 +++ src/testdir/test_vim9_func.vim | 14 ++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 6 +++++- 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/globals.h b/src/globals.h index 94ec54b9aa..51a69465c7 100644 --- a/src/globals.h +++ b/src/globals.h @@ -230,6 +230,9 @@ EXTERN int did_endif INIT(= FALSE); // just had ":endif" EXTERN int did_emsg; // set by emsg() when the message // is displayed or thrown #ifdef FEAT_EVAL +EXTERN int did_emsg_silent INIT(= 0); // incremented by emsg() when + // emsg_silent was set and did_emsg + // is not incremented EXTERN int did_emsg_def; // set by emsg() when emsg_silent // is set before calling a function EXTERN int did_emsg_cumul; // cumulative did_emsg, increased diff --git a/src/message.c b/src/message.c index 07c3943ab4..f2fe23b582 100644 --- a/src/message.c +++ b/src/message.c @@ -685,6 +685,9 @@ emsg_core(char_u *s) */ if (emsg_silent != 0) { +#ifdef FEAT_EVAL + ++did_emsg_silent; +#endif if (emsg_noredir == 0) { msg_start(); diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 6a3dfbbe6d..e254f44b73 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -2603,6 +2603,20 @@ def Test_compile_error() # Second call won't try compiling again assert_fails('call g:Broken()', 'E1091: Function is not compiled: Broken') + delfunc g:Broken + + # No error when compiling with :silent! + lines =<< trim END + def g:Broken() + echo 'a' + [] + enddef + silent! defcompile + END + CheckScriptSuccess(lines) + + # Calling the function won't try compiling again + assert_fails('call g:Broken()', 'E1091: Function is not compiled: Broken') + delfunc g:Broken enddef diff --git a/src/version.c b/src/version.c index 5cf56feadc..a22312a151 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2743, /**/ 2742, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index aa5894a355..e5e3068d5c 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -8429,6 +8429,7 @@ compile_def_function( cctx_T cctx; garray_T *instr; int did_emsg_before = did_emsg; + int did_emsg_silent_before = did_emsg_silent; int ret = FAIL; sctx_T save_current_sctx = current_sctx; int save_estack_compiling = estack_compiling; @@ -8967,6 +8968,9 @@ nextline: generate_instr(&cctx, ISN_RETURN_ZERO); } + // When compiled with ":silent!" and there was an error don't consider the + // function compiled. + if (emsg_silent == 0 || did_emsg_silent == did_emsg_silent_before) { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; @@ -8994,7 +8998,7 @@ nextline: ret = OK; erret: - if (ret == FAIL) + if (ufunc->uf_def_status == UF_COMPILING) { int idx; dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) From 962c43bf0d6a33b905f2acd920d3701476ebb5c9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 17:18:09 +0200 Subject: [PATCH 06/29] patch 8.2.2744: Vim9: no way to explicitly ignore an argument Problem: Vim9: no way to explicitly ignore an argument. Solution: Use the underscore as the name for an ignored argument. --- runtime/doc/vim9.txt | 29 ++++++++++++++++++++++++---- src/errors.h | 2 ++ src/eval.c | 7 ++++++- src/evalvars.c | 5 +++++ src/testdir/test_vim9_func.vim | 35 ++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 11 +++++++++++ 7 files changed, 86 insertions(+), 5 deletions(-) diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index e693cba1df..29fdab1488 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -137,18 +137,21 @@ arguments). Vim9 functions ~ A function defined with `:def` is compiled. Execution is many times faster, -often 10x to 100x times. +often 10 to 100 times. Many errors are already found when compiling, before the function is executed. The syntax is strict, to enforce code that is easy to read and understand. -Compilation is done when either of these is encountered: +Compilation is done when any of these is encountered: - the first time the function is called -- when the `:defcompile` command is encountered in the script where the +- when the `:defcompile` command is encountered in the script after the function was defined - `:disassemble` is used for the function. - a function that is compiled calls the function or uses it as a function reference + *E1091* +If compilation fails it is not tried again on the next call, instead this +error is given: "E1091: Function is not compiled: {name}". `:def` has no options like `:function` does: "range", "abort", "dict" or "closure". A `:def` function always aborts on an error (unless `:silent!` was @@ -161,7 +164,7 @@ functions. Arguments are accessed by name, without "a:", just like any other language. There is no "a:" dictionary or "a:000" list. - + *vim9-variable-arguments* Variable arguments are defined as the last argument, with a name and have a list type, similar to TypeScript. For example, a list of numbers: > def MyFunc(...itemlist: list) @@ -176,6 +179,15 @@ should use its default value. Example: > ... enddef MyFunc(v:none, 'LAST') # first argument uses default value 'one' +< + *vim9-ignored-argument* +The argument "_" (an underscore) can be used to ignore the argument. This is +most useful in callbacks where you don't need it, but do need to give an +argument to match the call. E.g. when using map() two arguments are passed, +the key and the value, to ignore the key: > + map(myList, (_, v) => v * 2) +There is no error for using the "_" argument multiple times. No type needs to +be given. Functions and variables are script-local by default ~ @@ -433,6 +445,15 @@ But you can use a backslash to concatenate the lines before parsing: > filter(list, (k, \ v) \ => v > 0) +< *vim9-lambda-arguments* +In legacy script a lambda could be called with any number of extra arguments, +there was no way to warn for not using them. In Vim9 script the number of +arguments must match. If you do want to accept any arguments, or any further +arguments, use "..._", which makes the function accept +|vim9-variable-arguments|. Example: > + var Callback = (..._) => 'anything' + echo Callback(1, 2, 3) # displays "anything" + < *inline-function* Additionally, a lambda can contain statements in {}: > var Lambda = (arg) => { diff --git a/src/errors.h b/src/errors.h index e4836bc4ac..d58da735aa 100644 --- a/src/errors.h +++ b/src/errors.h @@ -397,3 +397,5 @@ EXTERN char e_failed_to_extract_pwd_from_str_check_your_shell_config[] INIT(= N_("E1179: Failed to extract PWD from %s, check your shell's config related to OSC 7")); EXTERN char e_variable_arguments_type_must_be_list_str[] INIT(= N_("E1180: Variable arguments type must be a list: %s")); +EXTERN char e_cannot_use_underscore_here[] + INIT(= N_("E1181: Cannot use an underscore here")); diff --git a/src/eval.c b/src/eval.c index 05b6584424..fe9d4ec2e4 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3514,7 +3514,12 @@ eval7( { int flags = evalarg == NULL ? 0 : evalarg->eval_flags; - if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(') + if (in_vim9script() && len == 1 && *s == '_') + { + emsg(_(e_cannot_use_underscore_here)); + ret = FAIL; + } + else if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(') { // "name(..." recursive! *arg = skipwhite(*arg); diff --git a/src/evalvars.c b/src/evalvars.c index 67abdcb1d5..5869a82a61 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -3188,6 +3188,11 @@ set_var_const( goto failed; } var_in_vim9script = is_script_local && current_script_is_vim9(); + if (var_in_vim9script && name[0] == '_' && name[1] == NUL) + { + emsg(_(e_cannot_use_underscore_here)); + goto failed; + } di = find_var_in_ht(ht, 0, varname, TRUE); diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index e254f44b73..c02a324d4c 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -2619,6 +2619,41 @@ def Test_compile_error() delfunc g:Broken enddef +def Test_ignored_argument() + var lines =<< trim END + vim9script + def Ignore(_, _): string + return 'yes' + enddef + assert_equal('yes', Ignore(1, 2)) + + func Ok(_) + return a:_ + endfunc + assert_equal('ok', Ok('ok')) + + func Oktoo() + let _ = 'too' + return _ + endfunc + assert_equal('too', Oktoo()) + END + CheckScriptSuccess(lines) + + lines =<< trim END + def Ignore(_: string): string + return _ + enddef + defcompile + END + CheckScriptFailure(lines, 'E1181:', 1) + + lines =<< trim END + var _ = 1 + END + CheckDefAndScriptFailure(lines, 'E1181:', 1) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index a22312a151..d5a9dc7968 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2744, /**/ 2743, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index e5e3068d5c..b005ff2365 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4416,6 +4416,12 @@ compile_expr7( // "name" or "name()" p = to_name_end(*arg, TRUE); + if (p - *arg == (size_t)1 && **arg == '_') + { + emsg(_(e_cannot_use_underscore_here)); + return FAIL; + } + if (*p == '(') { r = compile_call(arg, p - *arg, cctx, ppconst, 0); @@ -6378,6 +6384,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) semsg(_(e_cannot_assign_to_constant), lhs.lhs_name); goto theend; } + if (is_decl && lhs.lhs_name[0] == '_' && lhs.lhs_name[1] == NUL) + { + emsg(_(e_cannot_use_underscore_here)); + goto theend; + } if (!heredoc) { From 51e7e78de7320a734ddc2d7931e767bccaf7e6f2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 17:46:52 +0200 Subject: [PATCH 07/29] patch 8.2.2745: Vim9: missing part of the argument change Problem: Vim9: missing part of the argument change. Solution: Add missing changes. --- src/userfunc.c | 24 +++++++++++++----------- src/version.c | 2 ++ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/userfunc.c b/src/userfunc.c index 8d06b4916c..018542681a 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -55,7 +55,7 @@ func_tbl_get(void) /* * Get one function argument. - * If "argtypes" is not NULL also get the type: "arg: type". + * If "argtypes" is not NULL also get the type: "arg: type" (:def function). * If "types_optional" is TRUE a missing type is OK, use "any". * If "evalarg" is not NULL use it to check for an already declared name. * Return a pointer to after the type. @@ -73,6 +73,7 @@ one_function_arg( { char_u *p = arg; char_u *arg_copy = NULL; + int is_underscore = FALSE; while (ASCII_ISALNUM(*p) || *p == '_') ++p; @@ -107,15 +108,16 @@ one_function_arg( *p = c; return arg; } - - // Check for duplicate argument name. - for (i = 0; i < newargs->ga_len; ++i) - if (STRCMP(((char_u **)(newargs->ga_data))[i], arg_copy) == 0) - { - semsg(_("E853: Duplicate argument name: %s"), arg_copy); - vim_free(arg_copy); - return arg; - } + is_underscore = arg_copy[0] == '_' && arg_copy[1] == NUL; + if (argtypes != NULL && !is_underscore) + // Check for duplicate argument name. + for (i = 0; i < newargs->ga_len; ++i) + if (STRCMP(((char_u **)(newargs->ga_data))[i], arg_copy) == 0) + { + semsg(_("E853: Duplicate argument name: %s"), arg_copy); + vim_free(arg_copy); + return arg; + } ((char_u **)(newargs->ga_data))[newargs->ga_len] = arg_copy; newargs->ga_len++; @@ -146,7 +148,7 @@ one_function_arg( if (!skip) type = vim_strnsave(type, p - type); } - else if (*skipwhite(p) != '=' && !types_optional) + else if (*skipwhite(p) != '=' && !types_optional && !is_underscore) { semsg(_(e_missing_argument_type_for_str), arg_copy == NULL ? arg : arg_copy); diff --git a/src/version.c b/src/version.c index d5a9dc7968..346b5d1f0d 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2745, /**/ 2744, /**/ From 87795939d01932b0d8155fd69c7494fa51c523f3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 18:21:30 +0200 Subject: [PATCH 08/29] patch 8.2.2746: check for duplicate arguments does not work Problem: Check for duplicate arguments does not work. Solution: Correct condition. --- src/userfunc.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/userfunc.c b/src/userfunc.c index 018542681a..5221126076 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -109,7 +109,7 @@ one_function_arg( return arg; } is_underscore = arg_copy[0] == '_' && arg_copy[1] == NUL; - if (argtypes != NULL && !is_underscore) + if (argtypes == NULL || !is_underscore) // Check for duplicate argument name. for (i = 0; i < newargs->ga_len; ++i) if (STRCMP(((char_u **)(newargs->ga_data))[i], arg_copy) == 0) diff --git a/src/version.c b/src/version.c index 346b5d1f0d..bbbc92abbb 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2746, /**/ 2745, /**/ From bb8a7ce0a1bcfafca715275dbadc2ead612f82dd Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 20:10:26 +0200 Subject: [PATCH 09/29] patch 8.2.2747: Vim9: not always an error for too many function arguments Problem: Vim9: not always an error for too many function arguments. Solution: Check for getting too many arguments. --- src/testdir/test_vim9_builtin.vim | 4 ++-- src/testdir/test_vim9_func.vim | 15 +++++++++++++-- src/version.c | 2 ++ src/vim9execute.c | 10 ++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index f537eef82c..5c13d51277 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -506,7 +506,7 @@ def Test_filter_wrong_dict_key_type() enddef def Test_filter_return_type() - var l = filter([1, 2, 3], () => 1) + var l = filter([1, 2, 3], (_, _) => 1) var res = 0 for n in l res += n @@ -516,7 +516,7 @@ enddef def Test_filter_missing_argument() var dict = {aa: [1], ab: [2], ac: [3], de: [4]} - var res = dict->filter((k) => k =~ 'a' && k !~ 'b') + var res = dict->filter((k, _) => k =~ 'a' && k !~ 'b') res->assert_equal({aa: [1], ac: [3]}) enddef diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index c02a324d4c..6b353dd699 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -2102,7 +2102,7 @@ def Test_script_var_in_lambda() var lines =<< trim END vim9script var script = 'test' - assert_equal(['test'], map(['one'], () => script)) + assert_equal(['test'], map(['one'], (_, _) => script)) END CheckScriptSuccess(lines) enddef @@ -2355,7 +2355,7 @@ def Test_block_scoped_var() var x = ['a', 'b', 'c'] if 1 var y = 'x' - map(x, () => y) + map(x, (_, _) => y) endif var z = x assert_equal(['x', 'x', 'x'], z) @@ -2654,6 +2654,17 @@ def Test_ignored_argument() CheckDefAndScriptFailure(lines, 'E1181:', 1) enddef +def Test_too_many_arguments() + var lines =<< trim END + echo [0, 1, 2]->map(() => 123) + END + CheckDefExecAndScriptFailure(lines, 'E1106: 2 arguments too many', 1) + + lines =<< trim END + echo [0, 1, 2]->map((_) => 123) + END + CheckDefExecAndScriptFailure(lines, 'E1106: One argument too many', 1) +enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index bbbc92abbb..9fca01a86d 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2747, /**/ 2746, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index a49d305532..e7885ad700 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1336,6 +1336,16 @@ call_def_function( ga_init2(&ectx.ec_trystack, sizeof(trycmd_T), 10); ga_init2(&ectx.ec_funcrefs, sizeof(partial_T *), 10); + idx = argc - ufunc->uf_args.ga_len; + if (idx > 0 && ufunc->uf_va_name == NULL) + { + if (idx == 1) + emsg(_(e_one_argument_too_many)); + else + semsg(_(e_nr_arguments_too_many), idx); + return FAIL; + } + // Put arguments on the stack, but no more than what the function expects. // A lambda can be called with more arguments than it uses. for (idx = 0; idx < argc From c4297697761230489a025b3e39b992cca845b1b3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 20:46:48 +0200 Subject: [PATCH 10/29] patch 8.2.2748: Vim9: memory leak when calling :def function fails Problem: Vim9: memory leak when calling :def function fails. Solution: Jump to failed_early instead of returning. --- src/version.c | 2 ++ src/vim9execute.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/version.c b/src/version.c index 9fca01a86d..fa3a9a8c65 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2748, /**/ 2747, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index e7885ad700..017d5774ef 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1343,7 +1343,7 @@ call_def_function( emsg(_(e_one_argument_too_many)); else semsg(_(e_nr_arguments_too_many), idx); - return FAIL; + goto failed_early; } // Put arguments on the stack, but no more than what the function expects. From fe95b94ffa75c4925ad16c43f94092f2b1d35fc6 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 20:52:43 +0200 Subject: [PATCH 11/29] patch 8.2.2749: Vim9: test for error can be a bit flaky Problem: Vim9: test for error can be a bit flaky. Solution: Increase the wait time a bit. --- src/testdir/test_vim9_script.vim | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 7155e8f3b0..e570210091 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -3681,7 +3681,7 @@ def Test_no_unknown_error_after_error() sleep 10m endwhile # wait for Exit_cb() to be called - sleep 100m + sleep 200m END writefile(lines, 'Xdef') assert_fails('so Xdef', ['E684:', 'E1012:']) diff --git a/src/version.c b/src/version.c index fa3a9a8c65..7785ac37cd 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2749, /**/ 2748, /**/ From da479c7597a61c4d50c842df21c9294bd9bf1037 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 21:01:38 +0200 Subject: [PATCH 12/29] patch 8.2.2750: Vim9: error for using underscore in nested function Problem: Vim9: error for using underscore in nested function. Solution: Do not consider "_" already defined. (closes #8096) --- src/testdir/test_vim9_func.vim | 2 ++ src/version.c | 2 ++ src/vim9compile.c | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 6b353dd699..a97146d45b 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -2637,6 +2637,8 @@ def Test_ignored_argument() return _ endfunc assert_equal('too', Oktoo()) + + assert_equal([[1], [2], [3]], range(3)->mapnew((_, v) => [v]->map((_, w) => w + 1))) END CheckScriptSuccess(lines) diff --git a/src/version.c b/src/version.c index 7785ac37cd..ed2237532d 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2750, /**/ 2749, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index b005ff2365..02c75c4d6a 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -422,6 +422,10 @@ check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg) int c = p[len]; ufunc_T *ufunc = NULL; + // underscore argument is OK + if (len == 1 && *p == '_') + return OK; + if (script_var_exists(p, len, cctx) == OK) { if (is_arg) From fed9e830fcffa90cf73dd125a27d27ed7eafbd68 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 21:38:38 +0200 Subject: [PATCH 13/29] patch 8.2.2751: Coverity warns for using NULL pointer Problem: Coverity warns for using NULL pointer. Solution: Check for NULL in calling function. --- src/userfunc.c | 6 ++++-- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/userfunc.c b/src/userfunc.c index 5221126076..54335d41f9 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -973,8 +973,7 @@ lambda_function_body( garray_T *default_args, char_u *ret_type) { - int evaluate = evalarg != NULL - && (evalarg->eval_flags & EVAL_EVALUATE); + int evaluate = (evalarg->eval_flags & EVAL_EVALUATE); ufunc_T *ufunc = NULL; exarg_T eap; garray_T newlines; @@ -1180,6 +1179,9 @@ get_lambda_tv( // Recognize "{" as the start of a function body. if (equal_arrow && **arg == '{') { + if (evalarg == NULL) + // cannot happen? + goto theend; if (lambda_function_body(arg, rettv, evalarg, pnewargs, types_optional ? &argtypes : NULL, varargs, &default_args, ret_type) == FAIL) diff --git a/src/version.c b/src/version.c index ed2237532d..2e0d3132bf 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2751, /**/ 2750, /**/ From e8e307818495d1a5d821df9bd4bde83add0520e5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 21:46:05 +0200 Subject: [PATCH 14/29] patch 8.2.2752 --- src/typval.c | 5 +---- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/typval.c b/src/typval.c index cf1208d586..f4af61a173 100644 --- a/src/typval.c +++ b/src/typval.c @@ -367,10 +367,7 @@ check_for_nonempty_string_arg(typval_T *args, int idx) return FAIL; if (args[idx].vval.v_string == NULL || *args[idx].vval.v_string == NUL) { - if (idx >= 0) - semsg(_(e_non_empty_string_required_for_argument_nr), idx + 1); - else - emsg(_(e_non_empty_string_required)); + semsg(_(e_non_empty_string_required_for_argument_nr), idx + 1); return FAIL; } return OK; diff --git a/src/version.c b/src/version.c index 2e0d3132bf..d266a1ad2e 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2752, /**/ 2751, /**/ From f93bbd026205f36915312193784f987ad49fb114 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 10 Apr 2021 22:35:43 +0200 Subject: [PATCH 15/29] patch 8.2.2753: Vim9: cannot ignore an item in assignment unpack Problem: Vim9: cannot ignore an item in assignment unpack. Solution: Allow using an underscore. --- runtime/doc/vim9.txt | 19 ++++++++++++------- src/eval.c | 2 +- src/evalvars.c | 12 +++++++----- src/testdir/test_vim9_assign.vim | 8 ++++++++ src/version.c | 2 ++ src/vim.h | 9 +++++---- src/vim9compile.c | 16 +++++++++++----- 7 files changed, 46 insertions(+), 22 deletions(-) diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index 29fdab1488..6b676dfabd 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -335,6 +335,18 @@ The "g:" prefix is not needed for auto-load functions. Since `&opt = value` is now assigning a value to option "opt", ":&" cannot be used to repeat a `:substitute` command. +For an unpack assignment the underscore can be used to ignore a list item, +similar to how a function argument can be ignored: > + [a, _, c] = theList + [a, b; _] = longList + +< *E1092* +Declaring more than one variable at a time, using the unpack notation, is +currently not supported: > + var [v1, v2] = GetValues() # Error! +That is because the type needs to be inferred from the list item type, which +isn't that easy. + Constants ~ *vim9-const* *vim9-final* @@ -368,13 +380,6 @@ The constant only applies to the value itself, not what it refers to. > NAMES[1] = ["Emma"] # Error! NAMES[1][0] = "Emma" # OK, now females[0] == "Emma" -< *E1092* -Declaring more than one variable at a time, using the unpack notation, is -currently not supported: > - var [v1, v2] = GetValues() # Error! -That is because the type needs to be inferred from the list item type, which -isn't that easy. - Omitting :call and :eval ~ diff --git a/src/eval.c b/src/eval.c index fe9d4ec2e4..787243ed65 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3514,7 +3514,7 @@ eval7( { int flags = evalarg == NULL ? 0 : evalarg->eval_flags; - if (in_vim9script() && len == 1 && *s == '_') + if (evaluate && in_vim9script() && len == 1 && *s == '_') { emsg(_(e_cannot_use_underscore_here)); ret = FAIL; diff --git a/src/evalvars.c b/src/evalvars.c index 5869a82a61..b8a4352f8a 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -970,8 +970,8 @@ ex_let_vars( { arg = skipwhite(arg + 1); ++var_idx; - arg = ex_let_one(arg, &item->li_tv, TRUE, flags, (char_u *)",;]", - op, var_idx); + arg = ex_let_one(arg, &item->li_tv, TRUE, + flags | ASSIGN_UNPACK, (char_u *)",;]", op, var_idx); item = item->li_next; if (arg == NULL) return FAIL; @@ -996,8 +996,8 @@ ex_let_vars( l->lv_refcount = 1; ++var_idx; - arg = ex_let_one(skipwhite(arg + 1), <v, FALSE, flags, - (char_u *)"]", op, var_idx); + arg = ex_let_one(skipwhite(arg + 1), <v, FALSE, + flags | ASSIGN_UNPACK, (char_u *)"]", op, var_idx); clear_tv(<v); if (arg == NULL) return FAIL; @@ -3190,7 +3190,9 @@ set_var_const( var_in_vim9script = is_script_local && current_script_is_vim9(); if (var_in_vim9script && name[0] == '_' && name[1] == NUL) { - emsg(_(e_cannot_use_underscore_here)); + // For "[a, _] = list" the underscore is ignored. + if ((flags & ASSIGN_UNPACK) == 0) + emsg(_(e_cannot_use_underscore_here)); goto failed; } diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index c35084d550..958b7b9809 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -256,6 +256,14 @@ def Test_assign_unpack() [v1, v2] = [1, 2] assert_equal(1, v1) assert_equal(2, v2) + + [v1, _, v2, _] = [1, 99, 2, 77] + assert_equal(1, v1) + assert_equal(2, v2) + + [v1, v2; _] = [1, 2, 3, 4, 5] + assert_equal(1, v1) + assert_equal(2, v2) END CheckDefAndScriptSuccess(lines) diff --git a/src/version.c b/src/version.c index d266a1ad2e..c760738bea 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2753, /**/ 2752, /**/ diff --git a/src/vim.h b/src/vim.h index 7f4c99ad36..31e181a13a 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2152,10 +2152,11 @@ typedef enum { } estack_arg_T; // Flags for assignment functions. -#define ASSIGN_FINAL 1 // ":final" -#define ASSIGN_CONST 2 // ":const" -#define ASSIGN_NO_DECL 4 // "name = expr" without ":let"/":const"/":final" -#define ASSIGN_DECL 8 // may declare variable if it does not exist +#define ASSIGN_FINAL 0x01 // ":final" +#define ASSIGN_CONST 0x02 // ":const" +#define ASSIGN_NO_DECL 0x04 // "name = expr" without ":let"/":const"/":final" +#define ASSIGN_DECL 0x08 // may declare variable if it does not exist +#define ASSIGN_UNPACK 0x10 // using [a, b] = list #include "ex_cmds.h" // Ex command defines #include "spell.h" // spell checking stuff diff --git a/src/vim9compile.c b/src/vim9compile.c index 02c75c4d6a..15d2c30e94 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -6369,6 +6369,17 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) { int instr_count = -1; + if (var_start[0] == '_' && !eval_isnamec(var_start[1])) + { + // Ignore underscore in "[a, _, b] = list". + if (var_count > 0) + { + var_start = skipwhite(var_start + 2); + continue; + } + emsg(_(e_cannot_use_underscore_here)); + goto theend; + } vim_free(lhs.lhs_name); /* @@ -6388,11 +6399,6 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) semsg(_(e_cannot_assign_to_constant), lhs.lhs_name); goto theend; } - if (is_decl && lhs.lhs_name[0] == '_' && lhs.lhs_name[1] == NUL) - { - emsg(_(e_cannot_use_underscore_here)); - goto theend; - } if (!heredoc) { From 09f067fca38c9f89ad088e8c096c4df3998575e2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 11 Apr 2021 13:29:18 +0200 Subject: [PATCH 16/29] patch 8.2.2754: :sleep! does not always hide the cursor Problem: :sleep! does not always hide the cursor. Solution: Add the cursor_is_asleep flag. (Jeremy Lerner, closes #8097, closes #7998) --- src/drawscreen.c | 5 +++-- src/ex_docmd.c | 5 ++++- src/gui.c | 5 +++++ src/proto/term.pro | 3 +++ src/term.c | 36 +++++++++++++++++++++++++++++++++++- src/version.c | 2 ++ 6 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/drawscreen.c b/src/drawscreen.c index 0e36e79173..7df4a96f0e 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -297,7 +297,9 @@ update_screen(int type_arg) // Remove the cursor before starting to do anything, because // scrolling may make it difficult to redraw the text under // it. - if (gui.in_use && wp == curwin) + // Also remove the cursor if it needs to be hidden due to an + // ongoing cursor-less sleep. + if (gui.in_use && (wp == curwin || cursor_is_sleeping())) { gui_cursor_col = gui.cursor_col; gui_cursor_row = gui.cursor_row; @@ -306,7 +308,6 @@ update_screen(int type_arg) } } #endif - win_update(wp); } diff --git a/src/ex_docmd.c b/src/ex_docmd.c index c8cb11ba75..7c1c448551 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -7370,7 +7370,7 @@ do_sleep(long msec, int hide_cursor) # endif if (hide_cursor) - cursor_off(); + cursor_sleep(); else cursor_on(); @@ -7422,6 +7422,9 @@ do_sleep(long msec, int hide_cursor) // input buffer, otherwise a following call to input() fails. if (got_int) (void)vpeekc(); + + if (hide_cursor) + cursor_unsleep(); } /* diff --git a/src/gui.c b/src/gui.c index 0265f7c964..c0374c561d 100644 --- a/src/gui.c +++ b/src/gui.c @@ -1120,6 +1120,11 @@ gui_update_cursor( || gui.row != gui.cursor_row || gui.col != gui.cursor_col) { gui_undraw_cursor(); + + // If a cursor-less sleep is ongoing, leave the cursor invisible + if (cursor_is_sleeping()) + return; + if (gui.row < 0) return; #ifdef HAVE_INPUT_METHOD diff --git a/src/proto/term.pro b/src/proto/term.pro index 1b3780204b..efb2555343 100644 --- a/src/proto/term.pro +++ b/src/proto/term.pro @@ -56,6 +56,9 @@ void scroll_start(void); void cursor_on_force(void); void cursor_on(void); void cursor_off(void); +int cursor_is_sleeping(void); +void cursor_sleep(void); +void cursor_unsleep(void); void term_cursor_mode(int forced); void term_cursor_color(char_u *color); int blink_state_is_inverted(void); diff --git a/src/term.c b/src/term.c index ad52d1b315..0a4e5b758a 100644 --- a/src/term.c +++ b/src/term.c @@ -3932,8 +3932,12 @@ scroll_start(void) } } +// True if cursor is not visible static int cursor_is_off = FALSE; +// True if cursor is not visible due to an ongoing cursor-less sleep +static int cursor_is_asleep = FALSE; + /* * Enable the cursor without checking if it's already enabled. */ @@ -3942,6 +3946,7 @@ cursor_on_force(void) { out_str(T_VE); cursor_is_off = FALSE; + cursor_is_asleep = FALSE; } /* @@ -3950,7 +3955,7 @@ cursor_on_force(void) void cursor_on(void) { - if (cursor_is_off) + if (cursor_is_off && !cursor_is_asleep) cursor_on_force(); } @@ -3967,6 +3972,35 @@ cursor_off(void) } } +/* + * Check whether the cursor is invisible due to an ongoing cursor-less sleep + */ + int +cursor_is_sleeping(void) +{ + return cursor_is_asleep; +} + +/* + * Disable the cursor and mark it disabled by cursor-less sleep + */ + void +cursor_sleep(void) +{ + cursor_is_asleep = TRUE; + cursor_off(); +} + +/* + * Enable the cursor and mark it not disabled by cursor-less sleep + */ + void +cursor_unsleep(void) +{ + cursor_is_asleep = FALSE; + cursor_on(); +} + #if defined(CURSOR_SHAPE) || defined(PROTO) /* * Set cursor shape to match Insert or Replace mode. diff --git a/src/version.c b/src/version.c index c760738bea..d18c0f8216 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2754, /**/ 2753, /**/ From af8ea0d066d31cf3cd0a39c5c49ce0342728588d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 11 Apr 2021 18:24:46 +0200 Subject: [PATCH 17/29] patch 8.2.2755: Vim9: no error for using a number in a condition Problem: Vim9: no error for using a number in a condition. Solution: Also use ISN_COND2BOOL if the type is t_number_bool. (closes #7644) --- src/testdir/test_vim9_disassemble.vim | 8 ++++---- src/testdir/test_vim9_expr.vim | 17 +++++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 2 +- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index e24e72f06b..363aaea356 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -770,7 +770,7 @@ def Test_disassemble_const_expr() 'if has("gui_running")\_s*' .. '\d PUSHS "gui_running"\_s*' .. '\d BCALL has(argc 1)\_s*' .. - '\d 2BOOL (!!val)\_s*' .. + '\d COND2BOOL\_s*' .. '\d JUMP_IF_FALSE -> \d\_s*' .. ' echo "yes"\_s*' .. '\d PUSHS "yes"\_s*' .. @@ -1537,13 +1537,13 @@ def Test_disassemble_return_bool() assert_match('ReturnBool\_s*' .. 'var name: bool = 1 && 0 || 1\_s*' .. '0 PUSHNR 1\_s*' .. - '1 2BOOL (!!val)\_s*' .. + '1 COND2BOOL\_s*' .. '2 JUMP_IF_COND_FALSE -> 5\_s*' .. '3 PUSHNR 0\_s*' .. - '4 2BOOL (!!val)\_s*' .. + '4 COND2BOOL\_s*' .. '5 JUMP_IF_COND_TRUE -> 8\_s*' .. '6 PUSHNR 1\_s*' .. - '7 2BOOL (!!val)\_s*' .. + '7 COND2BOOL\_s*' .. '\d STORE $0\_s*' .. 'return name\_s*' .. '\d\+ LOAD $0\_s*' .. diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 44ff105c62..0e0e34e01e 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -282,6 +282,20 @@ def Test_expr2() g:vals = [] assert_equal(false, Record(0) || Record(false) || Record(0)) assert_equal([0, false, 0], g:vals) + + g:vals = [] + var x = 1 + if x || true + g:vals = [1] + endif + assert_equal([1], g:vals) + + g:vals = [] + x = 3 + if true || x + g:vals = [1] + endif + assert_equal([1], g:vals) END CheckDefAndScriptSuccess(lines) enddef @@ -357,6 +371,9 @@ def Test_expr2_fails() # TODO: should fail at compile time call CheckDefExecAndScriptFailure(["var x = 3 || 7"], 'E1023:', 1) + call CheckDefAndScriptFailure(["if 3"], 'E1023:', 1) + call CheckDefExecAndScriptFailure(['var x = 3', 'if x', 'endif'], 'E1023:', 2) + call CheckDefAndScriptFailure2(["var x = [] || false"], 'E1012: Type mismatch; expected bool but got list', 'E745:', 1) enddef diff --git a/src/version.c b/src/version.c index d18c0f8216..d8b39eb226 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2755, /**/ 2754, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 15d2c30e94..59d1339073 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -990,7 +990,7 @@ bool_on_stack(cctx_T *cctx) if (type == &t_bool) return OK; - if (type == &t_any || type == &t_number) + if (type == &t_any || type == &t_number || type == &t_number_bool) // Number 0 and 1 are OK to use as a bool. "any" could also be a bool. // This requires a runtime type check. return generate_COND2BOOL(cctx); From cfc3023cb6ce5aaec13f49bc4b821feb05e3fb03 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 11 Apr 2021 20:26:34 +0200 Subject: [PATCH 18/29] patch 8.2.2756: Vim9: blob index and slice not implemented yet Problem: Vim9: blob index and slice not implemented yet. Solution: Implement blob index and slice. --- src/blob.c | 77 ++++++++++++++++++++++++++++++++++ src/eval.c | 64 +--------------------------- src/proto/blob.pro | 1 + src/testdir/test_vim9_expr.vim | 20 +++++++++ src/version.c | 2 + src/vim9.h | 2 + src/vim9compile.c | 18 ++++++-- src/vim9execute.c | 30 +++++++++---- 8 files changed, 142 insertions(+), 72 deletions(-) diff --git a/src/blob.c b/src/blob.c index 264962e197..8d260f172b 100644 --- a/src/blob.c +++ b/src/blob.c @@ -259,6 +259,83 @@ failed: return NULL; } + int +blob_slice_or_index( + blob_T *blob, + int is_range, + varnumber_T n1, + varnumber_T n2, + int exclusive, + typval_T *rettv) +{ + long len = blob_len(blob); + + if (is_range) + { + // The resulting variable is a sub-blob. If the indexes + // are out of range the result is empty. + if (n1 < 0) + { + n1 = len + n1; + if (n1 < 0) + n1 = 0; + } + if (n2 < 0) + n2 = len + n2; + else if (n2 >= len) + n2 = len - (exclusive ? 0 : 1); + if (exclusive) + --n2; + if (n1 >= len || n2 < 0 || n1 > n2) + { + clear_tv(rettv); + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = NULL; + } + else + { + blob_T *new_blob = blob_alloc(); + long i; + + if (new_blob != NULL) + { + if (ga_grow(&new_blob->bv_ga, n2 - n1 + 1) == FAIL) + { + blob_free(new_blob); + return FAIL; + } + new_blob->bv_ga.ga_len = n2 - n1 + 1; + for (i = n1; i <= n2; i++) + blob_set(new_blob, i - n1, blob_get(blob, i)); + + clear_tv(rettv); + rettv_blob_set(rettv, new_blob); + } + } + } + else + { + // The resulting variable is a byte value. + // If the index is too big or negative that is an error. + if (n1 < 0) + n1 = len + n1; + if (n1 < len && n1 >= 0) + { + int v = blob_get(blob, n1); + + clear_tv(rettv); + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = v; + } + else + { + semsg(_(e_blobidx), n1); + return FAIL; + } + } + return OK; +} + /* * "remove({blob})" function */ diff --git a/src/eval.c b/src/eval.c index 787243ed65..1291b364a9 100644 --- a/src/eval.c +++ b/src/eval.c @@ -4161,68 +4161,8 @@ eval_index_inner( break; case VAR_BLOB: - len = blob_len(rettv->vval.v_blob); - if (is_range) - { - // The resulting variable is a sub-blob. If the indexes - // are out of range the result is empty. - if (n1 < 0) - { - n1 = len + n1; - if (n1 < 0) - n1 = 0; - } - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len - (exclusive ? 0 : 1); - if (exclusive) - --n2; - if (n1 >= len || n2 < 0 || n1 > n2) - { - clear_tv(rettv); - rettv->v_type = VAR_BLOB; - rettv->vval.v_blob = NULL; - } - else - { - blob_T *blob = blob_alloc(); - long i; - - if (blob != NULL) - { - if (ga_grow(&blob->bv_ga, n2 - n1 + 1) == FAIL) - { - blob_free(blob); - return FAIL; - } - blob->bv_ga.ga_len = n2 - n1 + 1; - for (i = n1; i <= n2; i++) - blob_set(blob, i - n1, - blob_get(rettv->vval.v_blob, i)); - - clear_tv(rettv); - rettv_blob_set(rettv, blob); - } - } - } - else - { - // The resulting variable is a byte value. - // If the index is too big or negative that is an error. - if (n1 < 0) - n1 = len + n1; - if (n1 < len && n1 >= 0) - { - int v = blob_get(rettv->vval.v_blob, n1); - - clear_tv(rettv); - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = v; - } - else - semsg(_(e_blobidx), n1); - } + blob_slice_or_index(rettv->vval.v_blob, is_range, n1, n2, + exclusive, rettv); break; case VAR_LIST: diff --git a/src/proto/blob.pro b/src/proto/blob.pro index 3bc6625456..6be7f0bea5 100644 --- a/src/proto/blob.pro +++ b/src/proto/blob.pro @@ -13,5 +13,6 @@ int read_blob(FILE *fd, blob_T *blob); int write_blob(FILE *fd, blob_T *blob); char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf); blob_T *string2blob(char_u *str); +int blob_slice_or_index(blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, int exclusive, typval_T *rettv); void blob_remove(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 0e0e34e01e..456b42612e 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1622,6 +1622,26 @@ def Test_expr7_blob() assert_equal(g:blob_empty, 0z) assert_equal(g:blob_one, 0z01) assert_equal(g:blob_long, 0z0102.0304) + + var testblob = 0z010203 + assert_equal(0x01, testblob[0]) + assert_equal(0x02, testblob[1]) + assert_equal(0x03, testblob[-1]) + assert_equal(0x02, testblob[-2]) + + assert_equal(0z01, testblob[0 : 0]) + assert_equal(0z0102, testblob[0 : 1]) + assert_equal(0z010203, testblob[0 : 2]) + assert_equal(0z010203, testblob[0 : ]) + assert_equal(0z0203, testblob[1 : ]) + assert_equal(0z0203, testblob[1 : 2]) + assert_equal(0z0203, testblob[1 : -1]) + assert_equal(0z03, testblob[-1 : -1]) + assert_equal(0z02, testblob[-2 : -2]) + + # blob slice accepts out of range + assert_equal(0z, testblob[3 : 3]) + assert_equal(0z, testblob[0 : -4]) END CheckDefAndScriptSuccess(lines) diff --git a/src/version.c b/src/version.c index d8b39eb226..0dc382f43b 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2756, /**/ 2755, /**/ diff --git a/src/vim9.h b/src/vim9.h index 0c8a949e87..c30e597adb 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -133,6 +133,8 @@ typedef enum { ISN_LISTAPPEND, // append to a list, like add() ISN_LISTINDEX, // [expr] list index ISN_LISTSLICE, // [expr:expr] list slice + ISN_BLOBINDEX, // [expr] blob index + ISN_BLOBSLICE, // [expr:expr] blob slice ISN_ANYINDEX, // [expr] runtime index ISN_ANYSLICE, // [expr:expr] runtime slice ISN_SLICE, // drop isn_arg.number items from start of list diff --git a/src/vim9compile.c b/src/vim9compile.c index 59d1339073..9263659640 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2725,8 +2725,18 @@ compile_member(int is_slice, cctx_T *cctx) } else if (vtype == VAR_BLOB) { - emsg("Sorry, blob index and slice not implemented yet"); - return FAIL; + if (is_slice) + { + *typep = &t_blob; + if (generate_instr_drop(cctx, ISN_BLOBSLICE, 2) == FAIL) + return FAIL; + } + else + { + *typep = &t_number; + if (generate_instr_drop(cctx, ISN_BLOBINDEX, 1) == FAIL) + return FAIL; + } } else if (vtype == VAR_LIST || *typep == &t_any) { @@ -4088,7 +4098,7 @@ compile_subscript( // list index: list[123] // dict member: dict[key] // string index: text[123] - // TODO: blob index + // blob index: blob[123] // TODO: more arguments // TODO: recognize list or dict at runtime if (generate_ppconst(cctx, ppconst) == FAIL) @@ -9241,6 +9251,8 @@ delete_instr(isn_T *isn) case ISN_ANYSLICE: case ISN_BCALL: case ISN_BLOBAPPEND: + case ISN_BLOBINDEX: + case ISN_BLOBSLICE: case ISN_CATCH: case ISN_CHECKLEN: case ISN_CHECKNR: diff --git a/src/vim9execute.c b/src/vim9execute.c index 017d5774ef..94d6f45232 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -3415,16 +3415,21 @@ call_def_function( case ISN_LISTINDEX: case ISN_LISTSLICE: + case ISN_BLOBINDEX: + case ISN_BLOBSLICE: { - int is_slice = iptr->isn_type == ISN_LISTSLICE; - list_T *list; + int is_slice = iptr->isn_type == ISN_LISTSLICE + || iptr->isn_type == ISN_BLOBSLICE; + int is_blob = iptr->isn_type == ISN_BLOBINDEX + || iptr->isn_type == ISN_BLOBSLICE; varnumber_T n1, n2; + typval_T *val_tv; // list index: list is at stack-2, index at stack-1 // list slice: list is at stack-3, indexes at stack-2 and // stack-1 - tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2); - list = tv->vval.v_list; + // Same for blob. + val_tv = is_slice ? STACK_TV_BOT(-3) : STACK_TV_BOT(-2); tv = STACK_TV_BOT(-1); n1 = n2 = tv->vval.v_number; @@ -3440,9 +3445,18 @@ call_def_function( ectx.ec_stack.ga_len -= is_slice ? 2 : 1; tv = STACK_TV_BOT(-1); SOURCING_LNUM = iptr->isn_lnum; - if (list_slice_or_index(list, is_slice, n1, n2, FALSE, - tv, TRUE) == FAIL) - goto on_error; + if (is_blob) + { + if (blob_slice_or_index(val_tv->vval.v_blob, is_slice, + n1, n2, FALSE, tv) == FAIL) + goto on_error; + } + else + { + if (list_slice_or_index(val_tv->vval.v_list, is_slice, + n1, n2, FALSE, tv, TRUE) == FAIL) + goto on_error; + } } break; @@ -4688,6 +4702,8 @@ ex_disassemble(exarg_T *eap) case ISN_CONCAT: smsg("%4d CONCAT", current); break; case ISN_STRINDEX: smsg("%4d STRINDEX", current); break; case ISN_STRSLICE: smsg("%4d STRSLICE", current); break; + case ISN_BLOBINDEX: smsg("%4d BLOBINDEX", current); break; + case ISN_BLOBSLICE: smsg("%4d BLOBSLICE", current); break; case ISN_LISTAPPEND: smsg("%4d LISTAPPEND", current); break; case ISN_BLOBAPPEND: smsg("%4d BLOBAPPEND", current); break; case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break; From 68452177ca4cda4a9d5f93892e437447cf9404c8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 12 Apr 2021 21:21:02 +0200 Subject: [PATCH 19/29] patch 8.2.2757: Vim9: blob tests for legacy and Vim9 script are separate Problem: Vim9: blob tests for legacy and Vim9 script are separate. Solution: Add CheckLegacyAndVim9Success(). Make blob index assign work. --- src/blob.c | 22 +++++++ src/errors.h | 2 + src/eval.c | 15 +---- src/ex_docmd.c | 21 ++++--- src/proto/blob.pro | 1 + src/testdir/test_blob.vim | 122 +++++++++++++++++++++----------------- src/testdir/vim9.vim | 35 +++++++++++ src/version.c | 2 + src/vim9.h | 2 + src/vim9compile.c | 64 +++++++++++++++----- src/vim9execute.c | 62 +++++++++++++++++++ 11 files changed, 254 insertions(+), 94 deletions(-) diff --git a/src/blob.c b/src/blob.c index 8d260f172b..d758beb19d 100644 --- a/src/blob.c +++ b/src/blob.c @@ -336,6 +336,28 @@ blob_slice_or_index( return OK; } +/* + * Set bytes "n1" to "n2" (inclusive) in "dest" to the value of "src". + * Caller must make sure "src" is a blob. + * Returns FAIL if the number of bytes does not match. + */ + int +blob_set_range(blob_T *dest, long n1, long n2, typval_T *src) +{ + int il, ir; + + if (n2 - n1 + 1 != blob_len(src->vval.v_blob)) + { + emsg(_("E972: Blob value does not have the right number of bytes")); + return FAIL; + } + + ir = 0; + for (il = n1; il <= n2; il++) + blob_set(dest, il, blob_get(src->vval.v_blob, ir++)); + return OK; +} + /* * "remove({blob})" function */ diff --git a/src/errors.h b/src/errors.h index d58da735aa..d38562cfab 100644 --- a/src/errors.h +++ b/src/errors.h @@ -399,3 +399,5 @@ EXTERN char e_variable_arguments_type_must_be_list_str[] INIT(= N_("E1180: Variable arguments type must be a list: %s")); EXTERN char e_cannot_use_underscore_here[] INIT(= N_("E1181: Cannot use an underscore here")); +EXTERN char e_blob_required[] + INIT(= N_("E1182: Blob required")); diff --git a/src/eval.c b/src/eval.c index 1291b364a9..2e2ca23b69 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1319,23 +1319,12 @@ set_var_lval( if (lp->ll_range && rettv->v_type == VAR_BLOB) { - int il, ir; - if (lp->ll_empty2) lp->ll_n2 = blob_len(lp->ll_blob) - 1; - if (lp->ll_n2 - lp->ll_n1 + 1 != blob_len(rettv->vval.v_blob)) - { - emsg(_("E972: Blob value does not have the right number of bytes")); + if (blob_set_range(lp->ll_blob, lp->ll_n1, lp->ll_n2, + rettv) == FAIL) return; - } - if (lp->ll_empty2) - lp->ll_n2 = blob_len(lp->ll_blob); - - ir = 0; - for (il = lp->ll_n1; il <= lp->ll_n2; il++) - blob_set(lp->ll_blob, il, - blob_get(rettv->vval.v_blob, ir++)); } else { diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 7c1c448551..83f49d0d37 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3429,22 +3429,25 @@ find_ex_command( // "varname.key" is an expression. || (*p == '.' && ASCII_ISALPHA(p[1])))) { - char_u *after = p; + char_u *after = eap->cmd; // When followed by "=" or "+=" then it is an assignment. + // Skip over the whole thing, it can be: + // name.member = val + // name[a : b] = val + // name[idx] = val + // name[idx].member = val + // etc. + eap->cmdidx = CMD_eval; ++emsg_silent; - if (*after == '.') - after = skipwhite(after + 1); if (skip_expr(&after, NULL) == OK) + { after = skipwhite(after); - else - after = (char_u *)""; - if (*after == '=' || (*after != NUL && after[1] == '=') + if (*after == '=' || (*after != NUL && after[1] == '=') || (after[0] == '.' && after[1] == '.' && after[2] == '=')) - eap->cmdidx = CMD_var; - else - eap->cmdidx = CMD_eval; + eap->cmdidx = CMD_var; + } --emsg_silent; return eap->cmd; } diff --git a/src/proto/blob.pro b/src/proto/blob.pro index 6be7f0bea5..7da269c35f 100644 --- a/src/proto/blob.pro +++ b/src/proto/blob.pro @@ -14,5 +14,6 @@ int write_blob(FILE *fd, blob_T *blob); char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf); blob_T *string2blob(char_u *str); int blob_slice_or_index(blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, int exclusive, typval_T *rettv); +int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src); void blob_remove(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/testdir/test_blob.vim b/src/testdir/test_blob.vim index 3eff715d05..34ed0caf2f 100644 --- a/src/testdir/test_blob.vim +++ b/src/testdir/test_blob.vim @@ -1,5 +1,7 @@ " Tests for the Blob types +source vim9.vim + func TearDown() " Run garbage collection after every test call test_garbagecollect_now() @@ -9,73 +11,81 @@ endfunc " Blob creation from constant func Test_blob_create() - let b = 0zDEADBEEF - call assert_equal(v:t_blob, type(b)) - call assert_equal(4, len(b)) - call assert_equal(0xDE, b[0]) - call assert_equal(0xAD, b[1]) - call assert_equal(0xBE, b[2]) - call assert_equal(0xEF, b[3]) - call assert_fails('let x = b[4]') + let lines =<< trim END + VAR b = 0zDEADBEEF + call assert_equal(v:t_blob, type(b)) + call assert_equal(4, len(b)) + call assert_equal(0xDE, b[0]) + call assert_equal(0xAD, b[1]) + call assert_equal(0xBE, b[2]) + call assert_equal(0xEF, b[3]) + call assert_fails('VAR x = b[4]') - call assert_equal(0xDE, get(b, 0)) - call assert_equal(0xEF, get(b, 3)) + call assert_equal(0xDE, get(b, 0)) + call assert_equal(0xEF, get(b, 3)) - call assert_fails('let b = 0z1', 'E973:') - call assert_fails('let b = 0z1x', 'E973:') - call assert_fails('let b = 0z12345', 'E973:') + call assert_fails('VAR b = 0z1', 'E973:') + call assert_fails('VAR b = 0z1x', 'E973:') + call assert_fails('VAR b = 0z12345', 'E973:') - call assert_equal(0z, test_null_blob()) + call assert_equal(0z, test_null_blob()) - let b = 0z001122.33445566.778899.aabbcc.dd - call assert_equal(0z00112233445566778899aabbccdd, b) - call assert_fails('let b = 0z1.1') - call assert_fails('let b = 0z.') - call assert_fails('let b = 0z001122.') - call assert_fails('call get("", 1)', 'E896:') - call assert_equal(0, len(test_null_blob())) + LET b = 0z001122.33445566.778899.aabbcc.dd + call assert_equal(0z00112233445566778899aabbccdd, b) + call assert_fails('VAR b = 0z1.1') + call assert_fails('VAR b = 0z.') + call assert_fails('VAR b = 0z001122.') + call assert_fails('call get("", 1)', 'E896:') + call assert_equal(0, len(test_null_blob())) + END + call CheckLegacyAndVim9Success(lines) endfunc " assignment to a blob func Test_blob_assign() + let lines =<< trim END + VAR b = 0zDEADBEEF + VAR b2 = b[1 : 2] + call assert_equal(0zADBE, b2) + + VAR bcopy = b[:] + call assert_equal(b, bcopy) + call assert_false(b is bcopy) + + LET b = 0zDEADBEEF + LET b2 = b + call assert_true(b is b2) + LET b[:] = 0z11223344 + call assert_equal(0z11223344, b) + call assert_equal(0z11223344, b2) + call assert_true(b is b2) + + LET b = 0zDEADBEEF + LET b[3 :] = 0z66 + call assert_equal(0zDEADBE66, b) + LET b[: 1] = 0z8899 + call assert_equal(0z8899BE66, b) + + LET b = 0zDEADBEEF + LET b += 0z99 + call assert_equal(0zDEADBEEF99, b) + + VAR l = [0z12] + VAR m = deepcopy(l) + LET m[0] = 0z34 #" E742 or E741 should not occur. + END + call CheckLegacyAndVim9Success(lines) + + " TODO: move to above once it works let b = 0zDEADBEEF - let b2 = b[1:2] - call assert_equal(0zADBE, b2) + call assert_fails('let b[2 : 3] = 0z112233', 'E972:') + call assert_fails('let b[2 : 3] = 0z11', 'E972:') + call assert_fails('let b[3 : 2] = 0z', 'E979:') - let bcopy = b[:] - call assert_equal(b, bcopy) - call assert_false(b is bcopy) - - let b = 0zDEADBEEF - let b2 = b - call assert_true(b is b2) - let b[:] = 0z11223344 - call assert_equal(0z11223344, b) - call assert_equal(0z11223344, b2) - call assert_true(b is b2) - - let b = 0zDEADBEEF - let b[3:] = 0z66 - call assert_equal(0zDEADBE66, b) - let b[:1] = 0z8899 - call assert_equal(0z8899BE66, b) - - call assert_fails('let b[2:3] = 0z112233', 'E972:') - call assert_fails('let b[2:3] = 0z11', 'E972:') - call assert_fails('let b[3:2] = 0z', 'E979:') - - let b = 0zDEADBEEF - let b += 0z99 - call assert_equal(0zDEADBEEF99, b) - - call assert_fails('let b .= 0z33', 'E734:') - call assert_fails('let b .= "xx"', 'E734:') + call assert_fails('let b ..= 0z33', 'E734:') + call assert_fails('let b ..= "xx"', 'E734:') call assert_fails('let b += "xx"', 'E734:') - call assert_fails('let b[1:1] .= 0z55', 'E734:') - - let l = [0z12] - let m = deepcopy(l) - let m[0] = 0z34 " E742 or E741 should not occur. + call assert_fails('let b[1 : 1] ..= 0z55', 'E734:') endfunc func Test_blob_get_range() diff --git a/src/testdir/vim9.vim b/src/testdir/vim9.vim index f17f141dba..e05f8590bb 100644 --- a/src/testdir/vim9.vim +++ b/src/testdir/vim9.vim @@ -133,3 +133,38 @@ def CheckDefExecAndScriptFailure2( CheckDefExecFailure(lines, errorDef, lnum) CheckScriptFailure(['vim9script'] + lines, errorScript, lnum + 1) enddef + + +" Check that "lines" inside a legacy function has no error. +func CheckLegacySuccess(lines) + let cwd = getcwd() + let fname = 'XlegacySuccess' .. s:sequence + let s:sequence += 1 + call writefile(['func Func()'] + a:lines + ['endfunc'], fname) + try + exe 'so ' .. fname + call Func() + delfunc! Func + finally + call chdir(cwd) + call delete(fname) + endtry +endfunc + +" Execute "lines" in a legacy function, :def function and Vim9 script. +" Use 'VAR' for a declaration. +" Use 'LET' for an assignment +" Use ' #"' for a comment +def CheckLegacyAndVim9Success(lines: list) + var legacylines = lines->mapnew((_, v) => + v->substitute('\', 'let', 'g') + ->substitute('\', 'let', 'g') + ->substitute('#"', ' "', 'g')) + CheckLegacySuccess(legacylines) + + var vim9lines = lines->mapnew((_, v) => + v->substitute('\', 'var', 'g') + ->substitute('\lhs_varlen; char_u *p; int r = OK; + int need_white_before = TRUE; + int empty_second; p = var_start + varlen; if (*p == '[') { p = skipwhite(p + 1); - r = compile_expr0(&p, cctx); + if (*p == ':') + { + // empty first index, push zero + r = generate_PUSHNR(cctx, 0); + need_white_before = FALSE; + } + else + r = compile_expr0(&p, cctx); if (r == OK && *skipwhite(p) == ':') { // unlet var[idx : idx] - if (is_assign) - { - semsg(_(e_cannot_use_range_with_assignment_str), p); - return FAIL; - } + // blob[idx : idx] = value *range = TRUE; p = skipwhite(p); - if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1])) + empty_second = *skipwhite(p + 1) == ']'; + if ((need_white_before && !IS_WHITE_OR_NUL(p[-1])) + || (!empty_second && !IS_WHITE_OR_NUL(p[1]))) { semsg(_(e_white_space_required_before_and_after_str_at_str), ":", p); return FAIL; } p = skipwhite(p + 1); - r = compile_expr0(&p, cctx); + if (*p == ']') + // empty second index, push "none" + r = generate_PUSHSPEC(cctx, VVAL_NONE); + else + r = compile_expr0(&p, cctx); } if (r == OK && *skipwhite(p) != ']') @@ -6175,8 +6185,14 @@ compile_assign_unlet( garray_T *stack = &cctx->ctx_type_stack; int range = FALSE; - if (compile_assign_index(var_start, lhs, is_assign, &range, cctx) == FAIL) + if (compile_assign_index(var_start, lhs, &range, cctx) == FAIL) return FAIL; + if (is_assign && range && lhs->lhs_type != &t_blob + && lhs->lhs_type != &t_any) + { + semsg(_(e_cannot_use_range_with_assignment_str), var_start); + return FAIL; + } if (lhs->lhs_type == &t_any) { @@ -6213,15 +6229,24 @@ compile_assign_unlet( if (compile_load_lhs(lhs, var_start, rhs_type, cctx) == FAIL) return FAIL; - if (dest_type == VAR_LIST || dest_type == VAR_DICT || dest_type == VAR_ANY) + if (dest_type == VAR_LIST || dest_type == VAR_DICT + || dest_type == VAR_BLOB || dest_type == VAR_ANY) { if (is_assign) { - isn_T *isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3); + if (range) + { + if (generate_instr_drop(cctx, ISN_STORERANGE, 4) == NULL) + return FAIL; + } + else + { + isn_T *isn = generate_instr_drop(cctx, ISN_STOREINDEX, 3); - if (isn == NULL) - return FAIL; - isn->isn_arg.vartype = dest_type; + if (isn == NULL) + return FAIL; + isn->isn_arg.vartype = dest_type; + } } else if (range) { @@ -6443,8 +6468,14 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) // Get member from list or dict. First compile the // index value. if (compile_assign_index(var_start, &lhs, - TRUE, &range, cctx) == FAIL) + &range, cctx) == FAIL) goto theend; + if (range) + { + semsg(_(e_cannot_use_range_with_assignment_str), + var_start); + return FAIL; + } // Get the member. if (compile_member(FALSE, cctx) == FAIL) @@ -9315,6 +9346,7 @@ delete_instr(isn_T *isn) case ISN_SLICE: case ISN_STORE: case ISN_STOREINDEX: + case ISN_STORERANGE: case ISN_STORENR: case ISN_STOREOUTER: case ISN_STOREREG: diff --git a/src/vim9execute.c b/src/vim9execute.c index 94d6f45232..026a9ee45d 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2219,6 +2219,10 @@ call_def_function( clear_tv(tv); } } + else if (status == OK && dest_type == VAR_BLOB) + { + // TODO + } else { status = FAIL; @@ -2236,6 +2240,60 @@ call_def_function( } break; + // store value in blob range + case ISN_STORERANGE: + { + typval_T *tv_idx1 = STACK_TV_BOT(-3); + typval_T *tv_idx2 = STACK_TV_BOT(-2); + typval_T *tv_dest = STACK_TV_BOT(-1); + int status = OK; + + // Stack contains: + // -4 value to be stored + // -3 first index or "none" + // -2 second index or "none" + // -1 destination blob + tv = STACK_TV_BOT(-4); + if (tv_dest->v_type != VAR_BLOB) + { + status = FAIL; + emsg(_(e_blob_required)); + } + else + { + varnumber_T n1; + varnumber_T n2; + int error = FALSE; + + n1 = tv_get_number_chk(tv_idx1, &error); + if (error) + status = FAIL; + else + { + if (tv_idx2->v_type == VAR_SPECIAL + && tv_idx2->vval.v_number == VVAL_NONE) + n2 = blob_len(tv_dest->vval.v_blob) - 1; + else + n2 = tv_get_number_chk(tv_idx2, &error); + if (error) + status = FAIL; + else + status = blob_set_range(tv_dest->vval.v_blob, + n1, n2, tv); + } + } + + clear_tv(tv_idx1); + clear_tv(tv_idx2); + clear_tv(tv_dest); + ectx.ec_stack.ga_len -= 4; + clear_tv(tv); + + if (status == FAIL) + goto on_error; + } + break; + // load or store variable or argument from outer scope case ISN_LOADOUTER: case ISN_STOREOUTER: @@ -4362,6 +4420,10 @@ ex_disassemble(exarg_T *eap) } break; + case ISN_STORERANGE: + smsg("%4d STORERANGE", current); + break; + // constants case ISN_PUSHNR: smsg("%4d PUSHNR %lld", current, From f48b2fa33cda94e963f6fa8b78f344385c9ebea6 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 12 Apr 2021 22:02:36 +0200 Subject: [PATCH 20/29] patch 8.2.2758: Vim9: wrong line number for autoload function with wrong name Problem: Vim9: wrong line number for autoload function with wrong name. Solution: Set and restore SOURCING_LNUM. (closes #8100) --- src/testdir/test_vim9_func.vim | 24 ++++++++++++++++++++++++ src/userfunc.c | 4 ++++ src/version.c | 2 ++ 3 files changed, 30 insertions(+) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index a97146d45b..a941a61022 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -74,6 +74,30 @@ def TestCompilingErrorInTry() delete('Xdir', 'rf') enddef +def Test_autoload_name_mismatch() + var dir = 'Xdir/autoload' + mkdir(dir, 'p') + + var lines =<< trim END + vim9script + def scriptX#Function() + # comment + g:runtime = 'yes' + enddef + END + writefile(lines, dir .. '/script.vim') + + var save_rtp = &rtp + exe 'set rtp=' .. getcwd() .. '/Xdir' + lines =<< trim END + call script#Function() + END + CheckScriptFailure(lines, 'E746:', 2) + + &rtp = save_rtp + delete(dir, 'rf') +enddef + def CallRecursive(n: number): number return CallRecursive(n + 1) enddef diff --git a/src/userfunc.c b/src/userfunc.c index 54335d41f9..772d45d126 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -4058,7 +4058,11 @@ define_function(exarg_T *eap, char_u *name_arg) } if (j == FAIL) { + linenr_T save_lnum = SOURCING_LNUM; + + SOURCING_LNUM = sourcing_lnum_top; semsg(_("E746: Function name does not match script file name: %s"), name); + SOURCING_LNUM = save_lnum; goto erret; } } diff --git a/src/version.c b/src/version.c index 3728f320e6..4e61210536 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2758, /**/ 2757, /**/ From f2253963c28e4791092620df6a6bb238c33168df Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 13 Apr 2021 20:53:13 +0200 Subject: [PATCH 21/29] patch 8.2.2759: Vim9: for loop infers type of loop variable Problem: Vim9: for loop infers type of loop variable. Solution: Do not get the member type. (closes #8102) --- src/list.c | 4 +- src/proto/vim9script.pro | 2 +- src/proto/vim9type.pro | 2 +- src/testdir/test_vim9_script.vim | 151 +++++++++++++++++-------------- src/version.c | 2 + src/vim.h | 1 + src/vim9script.c | 9 +- src/vim9type.c | 26 ++++-- 8 files changed, 112 insertions(+), 85 deletions(-) diff --git a/src/list.c b/src/list.c index 76327abc87..56b2188ff4 100644 --- a/src/list.c +++ b/src/list.c @@ -2059,7 +2059,7 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) { // Check that map() does not change the type of the dict. ga_init2(&type_list, sizeof(type_T *), 10); - type = typval2type(argvars, get_copyID(), &type_list); + type = typval2type(argvars, get_copyID(), &type_list, TRUE); } if (argvars[0].v_type == VAR_BLOB) @@ -2565,7 +2565,7 @@ extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new) { // Check that map() does not change the type of the dict. ga_init2(&type_list, sizeof(type_T *), 10); - type = typval2type(argvars, get_copyID(), &type_list); + type = typval2type(argvars, get_copyID(), &type_list, TRUE); } if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro index c43120c452..cb5a30d2e0 100644 --- a/src/proto/vim9script.pro +++ b/src/proto/vim9script.pro @@ -12,7 +12,7 @@ void ex_import(exarg_T *eap); int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, int verbose); char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); -void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T *tv, type_T **type); +void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T *tv, type_T **type, int do_member); void hide_script_var(scriptitem_T *si, int idx, int func_defined); void free_all_script_vars(scriptitem_T *si); svar_T *find_typval_in_script(typval_T *dest); diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro index b5aa6ee921..a512fa4d55 100644 --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -9,7 +9,7 @@ type_T *alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap); type_T *get_func_type(type_T *ret_type, int argcount, garray_T *type_gap); int func_type_add_arg_types(type_T *functype, int argcount, garray_T *type_gap); int need_convert_to_bool(type_T *type, typval_T *tv); -type_T *typval2type(typval_T *tv, int copyID, garray_T *type_gap); +type_T *typval2type(typval_T *tv, int copyID, garray_T *type_gap, int do_member); type_T *typval2type_vimvar(typval_T *tv, garray_T *type_gap); int check_typval_arg_type(type_T *expected, typval_T *actual_tv, int arg_idx); int check_typval_type(type_T *expected, typval_T *actual_tv, where_T where); diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index e570210091..db1707ace6 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -2295,70 +2295,82 @@ def Test_for_outside_of_function() enddef def Test_for_loop() - var result = '' - for cnt in range(7) - if cnt == 4 - break - endif - if cnt == 2 - continue - endif - result ..= cnt .. '_' - endfor - assert_equal('0_1_3_', result) + var lines =<< trim END + var result = '' + for cnt in range(7) + if cnt == 4 + break + endif + if cnt == 2 + continue + endif + result ..= cnt .. '_' + endfor + assert_equal('0_1_3_', result) - var concat = '' - for str in eval('["one", "two"]') - concat ..= str - endfor - assert_equal('onetwo', concat) + var concat = '' + for str in eval('["one", "two"]') + concat ..= str + endfor + assert_equal('onetwo', concat) - var total = 0 - for nr in - [1, 2, 3] - total += nr - endfor - assert_equal(6, total) + var total = 0 + for nr in + [1, 2, 3] + total += nr + endfor + assert_equal(6, total) - total = 0 - for nr - in [1, 2, 3] - total += nr - endfor - assert_equal(6, total) + total = 0 + for nr + in [1, 2, 3] + total += nr + endfor + assert_equal(6, total) - total = 0 - for nr - in - [1, 2, 3] - total += nr - endfor - assert_equal(6, total) + total = 0 + for nr + in + [1, 2, 3] + total += nr + endfor + assert_equal(6, total) + # loop over string + var res = '' + for c in 'aéc̀d' + res ..= c .. '-' + endfor + assert_equal('a-é-c̀-d-', res) + + res = '' + for c in '' + res ..= c .. '-' + endfor + assert_equal('', res) + + res = '' + for c in test_null_string() + res ..= c .. '-' + endfor + assert_equal('', res) + + var foo: list> = [ + {a: 'Cat'} + ] + for dd in foo + dd.counter = 12 + endfor + assert_equal([{a: 'Cat', counter: 12}], foo) + END + CheckDefAndScriptSuccess(lines) + + # TODO: should also work at script level var res = "" for [n: number, s: string] in [[1, 'a'], [2, 'b']] res ..= n .. s endfor assert_equal('1a2b', res) - - # loop over string - res = '' - for c in 'aéc̀d' - res ..= c .. '-' - endfor - assert_equal('a-é-c̀-d-', res) - - res = '' - for c in '' - res ..= c .. '-' - endfor - assert_equal('', res) - - res = '' - for c in test_null_string() - res ..= c .. '-' - endfor - assert_equal('', res) enddef def Test_for_loop_fails() @@ -2471,20 +2483,23 @@ def Test_for_loop_unpack() enddef def Test_for_loop_with_try_continue() - var looped = 0 - var cleanup = 0 - for i in range(3) - looped += 1 - try - eval [][0] - catch - continue - finally - cleanup += 1 - endtry - endfor - assert_equal(3, looped) - assert_equal(3, cleanup) + var lines =<< trim END + var looped = 0 + var cleanup = 0 + for i in range(3) + looped += 1 + try + eval [][0] + catch + continue + finally + cleanup += 1 + endtry + endfor + assert_equal(3, looped) + assert_equal(3, cleanup) + END + CheckDefAndScriptSuccess(lines) enddef def Test_while_loop() diff --git a/src/version.c b/src/version.c index 4e61210536..fdb8d0a9b5 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2759, /**/ 2758, /**/ diff --git a/src/vim.h b/src/vim.h index 31e181a13a..baa3526d0f 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2157,6 +2157,7 @@ typedef enum { #define ASSIGN_NO_DECL 0x04 // "name = expr" without ":let"/":const"/":final" #define ASSIGN_DECL 0x08 // may declare variable if it does not exist #define ASSIGN_UNPACK 0x10 // using [a, b] = list +#define ASSIGN_NO_MEMBER_TYPE 0x20 // use "any" for list and dict member type #include "ex_cmds.h" // Ex command defines #include "spell.h" // spell checking stuff diff --git a/src/vim9script.c b/src/vim9script.c index 2f2f2dce67..796b7d13cb 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -713,7 +713,8 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg) * When "create" is TRUE this is a new variable, otherwise find and update an * existing variable. * "flags" can have ASSIGN_FINAL or ASSIGN_CONST. - * When "*type" is NULL use "tv" for the type and update "*type". + * When "*type" is NULL use "tv" for the type and update "*type". If + * "do_member" is TRUE also use the member type, otherwise use "any". */ void update_vim9_script_var( @@ -721,7 +722,8 @@ update_vim9_script_var( dictitem_T *di, int flags, typval_T *tv, - type_T **type) + type_T **type, + int do_member) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); hashitem_T *hi; @@ -774,7 +776,8 @@ update_vim9_script_var( if (sv != NULL) { if (*type == NULL) - *type = typval2type(tv, get_copyID(), &si->sn_type_list); + *type = typval2type(tv, get_copyID(), &si->sn_type_list, + do_member); sv->sv_type = *type; } diff --git a/src/vim9type.c b/src/vim9type.c index 2485efb41e..8b0f2f15e2 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -252,9 +252,10 @@ func_type_add_arg_types( /* * Get a type_T for a typval_T. * "type_gap" is used to temporarily create types in. + * When "do_member" is TRUE also get the member type, otherwise use "any". */ static type_T * -typval2type_int(typval_T *tv, int copyID, garray_T *type_gap) +typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int do_member) { type_T *type; type_T *member_type = &t_any; @@ -274,6 +275,8 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap) if (l == NULL || l->lv_first == NULL) return &t_list_empty; + if (!do_member) + return &t_list_any; if (l->lv_first == &range_list_item) return &t_list_number; if (l->lv_copyID == copyID) @@ -282,9 +285,9 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap) l->lv_copyID = copyID; // Use the common type of all members. - member_type = typval2type(&l->lv_first->li_tv, copyID, type_gap); + member_type = typval2type(&l->lv_first->li_tv, copyID, type_gap, TRUE); for (li = l->lv_first->li_next; li != NULL; li = li->li_next) - common_type(typval2type(&li->li_tv, copyID, type_gap), + common_type(typval2type(&li->li_tv, copyID, type_gap, TRUE), member_type, &member_type, type_gap); return get_list_type(member_type, type_gap); } @@ -297,6 +300,8 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap) if (d == NULL || d->dv_hashtab.ht_used == 0) return &t_dict_empty; + if (!do_member) + return &t_dict_any; if (d->dv_copyID == copyID) // avoid recursion return &t_dict_any; @@ -305,9 +310,9 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap) // Use the common type of all values. dict_iterate_start(tv, &iter); dict_iterate_next(&iter, &value); - member_type = typval2type(value, copyID, type_gap); + member_type = typval2type(value, copyID, type_gap, TRUE); while (dict_iterate_next(&iter, &value) != NULL) - common_type(typval2type(value, copyID, type_gap), + common_type(typval2type(value, copyID, type_gap, TRUE), member_type, &member_type, type_gap); return get_dict_type(member_type, type_gap); } @@ -378,11 +383,12 @@ need_convert_to_bool(type_T *type, typval_T *tv) /* * Get a type_T for a typval_T. * "type_list" is used to temporarily create types in. + * When "do_member" is TRUE also get the member type, otherwise use "any". */ type_T * -typval2type(typval_T *tv, int copyID, garray_T *type_gap) +typval2type(typval_T *tv, int copyID, garray_T *type_gap, int do_member) { - type_T *type = typval2type_int(tv, copyID, type_gap); + type_T *type = typval2type_int(tv, copyID, type_gap, do_member); if (type != NULL && type != &t_bool && (tv->v_type == VAR_NUMBER @@ -404,7 +410,7 @@ typval2type_vimvar(typval_T *tv, garray_T *type_gap) return &t_list_string; if (tv->v_type == VAR_DICT) // e.g. for v:completed_item return &t_dict_any; - return typval2type(tv, get_copyID(), type_gap); + return typval2type(tv, get_copyID(), type_gap, TRUE); } int @@ -429,7 +435,7 @@ check_typval_type(type_T *expected, typval_T *actual_tv, where_T where) int res = FAIL; ga_init2(&type_list, sizeof(type_T *), 10); - actual_type = typval2type(actual_tv, get_copyID(), &type_list); + actual_type = typval2type(actual_tv, get_copyID(), &type_list, TRUE); if (actual_type != NULL) res = check_type(expected, actual_type, TRUE, where); clear_type_list(&type_list); @@ -1210,7 +1216,7 @@ f_typename(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_STRING; ga_init2(&type_list, sizeof(type_T *), 10); - type = typval2type(argvars, get_copyID(), &type_list); + type = typval2type(argvars, get_copyID(), &type_list, TRUE); name = type_name(type, &tofree); if (tofree != NULL) rettv->vval.v_string = (char_u *)tofree; From f6a8d420a8d2924737f713de046947dcb487550c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 13 Apr 2021 21:48:03 +0200 Subject: [PATCH 22/29] patch 8.2.2760: Vim9: no error for changing a for loop variable Problem: Vim9: no error for changing a for loop variable. Solution: Make the loop variable read-only. (issue #8102) --- src/eval.c | 13 +++++++++---- src/evalvars.c | 27 ++++++++++++++++++++------- src/testdir/test_vim9_script.vim | 8 ++++++++ src/version.c | 2 ++ src/vim.h | 1 + src/vim9compile.c | 2 +- 6 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/eval.c b/src/eval.c index 2e2ca23b69..99768ee280 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1351,7 +1351,8 @@ set_var_lval( { typval_T tv; - if (flags & (ASSIGN_CONST | ASSIGN_FINAL)) + if ((flags & (ASSIGN_CONST | ASSIGN_FINAL)) + && (flags & ASSIGN_FOR_LOOP) == 0) { emsg(_(e_cannot_mod)); *endp = cc; @@ -1390,7 +1391,8 @@ set_var_lval( listitem_T *ll_li = lp->ll_li; int ll_n1 = lp->ll_n1; - if (flags & (ASSIGN_CONST | ASSIGN_FINAL)) + if ((flags & (ASSIGN_CONST | ASSIGN_FINAL)) + && (flags & ASSIGN_FOR_LOOP) == 0) { emsg(_("E996: Cannot lock a range")); return; @@ -1449,7 +1451,8 @@ set_var_lval( /* * Assign to a List or Dictionary item. */ - if (flags & (ASSIGN_CONST | ASSIGN_FINAL)) + if ((flags & (ASSIGN_CONST | ASSIGN_FINAL)) + && (flags & ASSIGN_FOR_LOOP) == 0) { emsg(_("E996: Cannot lock a list or dict")); return; @@ -1775,7 +1778,9 @@ next_for_item(void *fi_void, char_u *arg) { forinfo_T *fi = (forinfo_T *)fi_void; int result; - int flag = in_vim9script() ? ASSIGN_DECL : 0; + int flag = ASSIGN_FOR_LOOP | (in_vim9script() + ? (ASSIGN_FINAL | ASSIGN_DECL | ASSIGN_NO_MEMBER_TYPE) + : 0); listitem_T *item; if (fi->fi_blob != NULL) diff --git a/src/evalvars.c b/src/evalvars.c index b8a4352f8a..eb9ad6da16 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -1315,7 +1315,8 @@ ex_let_one( // ":let $VAR = expr": Set environment variable. if (*arg == '$') { - if (flags & (ASSIGN_CONST | ASSIGN_FINAL)) + if ((flags & (ASSIGN_CONST | ASSIGN_FINAL)) + && (flags & ASSIGN_FOR_LOOP) == 0) { emsg(_("E996: Cannot lock an environment variable")); return NULL; @@ -1365,9 +1366,11 @@ ex_let_one( // ":let &option = expr": Set option value. // ":let &l:option = expr": Set local option value. // ":let &g:option = expr": Set global option value. + // ":for &ts in range(8)": Set option value for for loop else if (*arg == '&') { - if (flags & (ASSIGN_CONST | ASSIGN_FINAL)) + if ((flags & (ASSIGN_CONST | ASSIGN_FINAL)) + && (flags & ASSIGN_FOR_LOOP) == 0) { emsg(_(e_const_option)); return NULL; @@ -1466,7 +1469,8 @@ ex_let_one( // ":let @r = expr": Set register contents. else if (*arg == '@') { - if (flags & (ASSIGN_CONST | ASSIGN_FINAL)) + if ((flags & (ASSIGN_CONST | ASSIGN_FINAL)) + && (flags & ASSIGN_FOR_LOOP) == 0) { emsg(_("E996: Cannot lock a register")); return NULL; @@ -3158,7 +3162,7 @@ set_var_const( type_T *type, typval_T *tv_arg, int copy, // make copy of value in "tv" - int flags, // ASSIGN_CONST, ASSIGN_FINAL, etc. + int flags_arg, // ASSIGN_CONST, ASSIGN_FINAL, etc. int var_idx) // index for ":let [a, b] = list" { typval_T *tv = tv_arg; @@ -3169,6 +3173,7 @@ set_var_const( int is_script_local; int vim9script = in_vim9script(); int var_in_vim9script; + int flags = flags_arg; ht = find_var_ht(name, &varname); if (ht == NULL || *varname == NUL) @@ -3187,6 +3192,11 @@ set_var_const( vim9_declare_error(name); goto failed; } + if ((flags & ASSIGN_FOR_LOOP) && name[1] == ':' + && vim_strchr((char_u *)"gwbt", name[0]) != NULL) + // Do not make g:var, w:var, b:var or t:var final. + flags &= ~ASSIGN_FINAL; + var_in_vim9script = is_script_local && current_script_is_vim9(); if (var_in_vim9script && name[0] == '_' && name[1] == NUL) { @@ -3220,7 +3230,8 @@ set_var_const( // Item already exists. Allowed to replace when reloading. if ((di->di_flags & DI_FLAGS_RELOAD) == 0) { - if (flags & (ASSIGN_CONST | ASSIGN_FINAL)) + if ((flags & (ASSIGN_CONST | ASSIGN_FINAL)) + && (flags & ASSIGN_FOR_LOOP) == 0) { emsg(_(e_cannot_mod)); goto failed; @@ -3255,7 +3266,8 @@ set_var_const( // A Vim9 script-local variable is also present in sn_all_vars and // sn_var_vals. It may set "type" from "tv". if (var_in_vim9script) - update_vim9_script_var(FALSE, di, flags, tv, &type); + update_vim9_script_var(FALSE, di, flags, tv, &type, + (flags & ASSIGN_NO_MEMBER_TYPE) == 0); } // existing variable, need to clear the value @@ -3353,7 +3365,8 @@ set_var_const( // A Vim9 script-local variable is also added to sn_all_vars and // sn_var_vals. It may set "type" from "tv". if (var_in_vim9script) - update_vim9_script_var(TRUE, di, flags, tv, &type); + update_vim9_script_var(TRUE, di, flags, tv, &type, + (flags & ASSIGN_NO_MEMBER_TYPE) == 0); } if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index db1707ace6..af7e5fdbb6 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -2393,6 +2393,14 @@ def Test_for_loop_fails() g:adict = {a: 1} CheckDefExecFailure(['for i in g:adict', 'echo 3', 'endfor'], 'E1177: For loop on dict not supported') unlet g:adict + + var lines =<< trim END + var d: list> = [{a: 0}] + for e in d + e = {a: 0, b: ''} + endfor + END + CheckDefAndScriptFailure2(lines, 'E1018:', 'E46:', 3) enddef def Test_for_loop_script_var() diff --git a/src/version.c b/src/version.c index fdb8d0a9b5..4861ba1a9f 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2760, /**/ 2759, /**/ diff --git a/src/vim.h b/src/vim.h index baa3526d0f..4c9e095a10 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2158,6 +2158,7 @@ typedef enum { #define ASSIGN_DECL 0x08 // may declare variable if it does not exist #define ASSIGN_UNPACK 0x10 // using [a, b] = list #define ASSIGN_NO_MEMBER_TYPE 0x20 // use "any" for list and dict member type +#define ASSIGN_FOR_LOOP 0x40 // assigning to loop variable #include "ex_cmds.h" // Ex command defines #include "spell.h" // spell checking stuff diff --git a/src/vim9compile.c b/src/vim9compile.c index bcba1e3224..4a55f1973b 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -7590,7 +7590,7 @@ compile_for(char_u *arg_start, cctx_T *cctx) // Reserve a variable to store "var". // TODO: check for type - var_lvar = reserve_local(cctx, arg, varlen, FALSE, &t_any); + var_lvar = reserve_local(cctx, arg, varlen, TRUE, &t_any); if (var_lvar == NULL) // out of memory or used as an argument goto failed; From 2e240bd428c0033d16f201d7f837636412358199 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 14 Apr 2021 11:15:08 +0200 Subject: [PATCH 23/29] patch 8.2.2761: using "syn include" does not work properly Problem: Using "syn include" does not work properly. Solution: Don't add current_syn_inc_tag to topgrp. (Jaehwang Jerry Jung, closes #8104) --- src/syntax.c | 13 +++++++++---- src/testdir/test_syntax.vim | 17 +++++++++++++++++ src/version.c | 2 ++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/syntax.c b/src/syntax.c index d4ec0d339f..c3572d8435 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -5990,12 +5990,17 @@ get_id_list( break; } if (name[1] == 'A') - id = SYNID_ALLBUT; + id = SYNID_ALLBUT + current_syn_inc_tag; else if (name[1] == 'T') - id = SYNID_TOP; + { + if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER) + id = curwin->w_s->b_syn_topgrp; + else + id = SYNID_TOP + current_syn_inc_tag; + } else - id = SYNID_CONTAINED; - id += current_syn_inc_tag; + id = SYNID_CONTAINED + current_syn_inc_tag; + } else if (name[1] == '@') { diff --git a/src/testdir/test_syntax.vim b/src/testdir/test_syntax.vim index 1a413f6ea9..bc268a14bb 100644 --- a/src/testdir/test_syntax.vim +++ b/src/testdir/test_syntax.vim @@ -920,4 +920,21 @@ func Test_syn_contained_transparent() bw! endfunc +func Test_syn_include_contains_TOP() + let l:case = "TOP in included syntax means its group list name" + new + syntax include @INCLUDED syntax/c.vim + syntax region FencedCodeBlockC start=/```c/ end=/```/ contains=@INCLUDED + + call setline(1, ['```c', '#if 0', 'int', '#else', 'int', '#endif', '```' ]) + let l:expected = ["cCppOutIf2"] + eval AssertHighlightGroups(3, 1, l:expected, 1) + " cCppOutElse has contains=TOP + let l:expected = ["cType"] + eval AssertHighlightGroups(5, 1, l:expected, 1, l:case) + syntax clear + bw! +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 4861ba1a9f..5372cb46cb 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2761, /**/ 2760, /**/ From f62d73933af7830301989eb8162ce94a80e61fbf Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 14 Apr 2021 12:40:00 +0200 Subject: [PATCH 24/29] patch 8.2.2762: Vim9: function line truncated when compiling Problem: Vim9: function line truncated when compiling. Solution: Copy the line before processing it. (closes #8101) --- src/testdir/test_vim9_disassemble.vim | 39 +++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 10 +++++++ 3 files changed, 51 insertions(+) diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 363aaea356..3ea2ad9a40 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -386,6 +386,33 @@ def Test_disassemble_blob_add() res) enddef +def s:BlobIndexSlice() + var b: blob = 0z112233 + echo b[1] + echo b[1 : 2] +enddef + +def Test_disassemble_blob_index_slice() + var res = execute('disass s:BlobIndexSlice') + assert_match('\d*_BlobIndexSlice\_s*' .. + 'var b: blob = 0z112233\_s*' .. + '\d PUSHBLOB 0z112233\_s*' .. + '\d STORE $0\_s*' .. + 'echo b\[1\]\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d BLOBINDEX\_s*' .. + '\d ECHO 1\_s*' .. + 'echo b\[1 : 2\]\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 1\_s*' .. + '\d\+ PUSHNR 2\_s*' .. + '\d\+ BLOBSLICE\_s*' .. + '\d\+ ECHO 1\_s*' .. + '\d\+ RETURN 0', + res) +enddef + def s:ScriptFuncUnlet() g:somevar = "value" unlet g:somevar @@ -2018,5 +2045,17 @@ def Test_profiled() res) enddef +def s:EchoMessages() + echohl ErrorMsg | echom v:exception | echohl NONE +enddef + +def Test_disassemble_nextcmd() + # splitting commands and removing trailing blanks should not change the line + var res = execute('disass s:EchoMessages') + assert_match('\d*_EchoMessages\_s*' .. + 'echohl ErrorMsg | echom v:exception | echohl NONE', + res) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 5372cb46cb..72da5f91ba 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2762, /**/ 2761, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 4a55f1973b..0ada441a4b 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -8486,6 +8486,7 @@ compile_def_function( cctx_T *outer_cctx) { char_u *line = NULL; + char_u *line_to_free = NULL; char_u *p; char *errormsg = NULL; // error message cctx_T cctx; @@ -8647,6 +8648,14 @@ compile_def_function( #endif break; } + // Make a copy, splitting off nextcmd and removing trailing spaces + // may change it. + if (line != NULL) + { + line = vim_strsave(line); + vim_free(line_to_free); + line_to_free = line; + } } CLEAR_FIELD(ea); @@ -9095,6 +9104,7 @@ erret: if (do_estack_push) estack_pop(); + vim_free(line_to_free); free_imported(&cctx); free_locals(&cctx); ga_clear(&cctx.ctx_type_stack); From a3589a0d6cdb314e70421c0f2e5a2d1abf68e597 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 14 Apr 2021 13:30:46 +0200 Subject: [PATCH 25/29] patch 8.2.2763: Vim9: cannot use type in for loop unpack at script level Problem: Vim9: cannot use type in for loop unpack at script level. Solution: Advance over the type name. --- src/evalvars.c | 2 +- src/testdir/test_vim9_script.vim | 23 +++++++++++++++-------- src/version.c | 2 ++ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/evalvars.c b/src/evalvars.c index eb9ad6da16..ebfc42b187 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -1523,7 +1523,7 @@ ex_let_one( else { set_var_lval(&lv, p, tv, copy, flags, op, var_idx); - arg_end = p; + arg_end = lv.ll_name_end; } } clear_lval(&lv); diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index af7e5fdbb6..03da2fd86f 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -2336,8 +2336,22 @@ def Test_for_loop() endfor assert_equal(6, total) - # loop over string + # with type + total = 0 + for n: number in [1, 2, 3] + total += n + endfor + assert_equal(6, total) + + # unpack with type var res = '' + for [n: number, s: string] in [[1, 'a'], [2, 'b']] + res ..= n .. s + endfor + assert_equal('1a2b', res) + + # loop over string + res = '' for c in 'aéc̀d' res ..= c .. '-' endfor @@ -2364,13 +2378,6 @@ def Test_for_loop() assert_equal([{a: 'Cat', counter: 12}], foo) END CheckDefAndScriptSuccess(lines) - - # TODO: should also work at script level - var res = "" - for [n: number, s: string] in [[1, 'a'], [2, 'b']] - res ..= n .. s - endfor - assert_equal('1a2b', res) enddef def Test_for_loop_fails() diff --git a/src/version.c b/src/version.c index 72da5f91ba..3dee026c1d 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2763, /**/ 2762, /**/ From b47bed2f7ada4dfae78f76f27473b83507e40315 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 14 Apr 2021 17:06:43 +0200 Subject: [PATCH 26/29] patch 8.2.2764: memory leak when default function argument is allocated Problem: Memory leak when default function argument is allocated. Solution: Free the expression result. --- src/testdir/test_functions.vim | 9 +++++++++ src/userfunc.c | 9 +++++++++ src/version.c | 2 ++ 3 files changed, 20 insertions(+) diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index 8ee36794a4..1cce8a0b19 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -1497,6 +1497,7 @@ endfunc func Test_balloon_show() CheckFeature balloon_eval + " This won't do anything but must not crash either. call balloon_show('hi!') if !has('gui_running') @@ -2650,4 +2651,12 @@ func Test_browsedir() call assert_fails('call browsedir("open", [])', 'E730:') endfunc +func HasDefault(msg = 'msg') + return a:msg +endfunc + +func Test_default_arg_value() + call assert_equal('msg', HasDefault()) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/userfunc.c b/src/userfunc.c index 772d45d126..99d4e0334e 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -2188,6 +2188,8 @@ call_user_func( int islambda = FALSE; char_u numbuf[NUMBUFLEN]; char_u *name; + typval_T *tv_to_free[MAX_FUNC_ARGS]; + int tv_to_free_len = 0; #ifdef FEAT_PROFILE profinfo_T profile_info; #endif @@ -2333,6 +2335,7 @@ call_user_func( if (isdefault) { char_u *default_expr = NULL; + def_rettv.v_type = VAR_NUMBER; def_rettv.vval.v_number = -1; @@ -2374,6 +2377,10 @@ call_user_func( v->di_tv = isdefault ? def_rettv : argvars[i]; v->di_tv.v_lock = VAR_FIXED; + if (isdefault) + // Need to free this later, no matter where it's stored. + tv_to_free[tv_to_free_len++] = &v->di_tv; + if (addlocal) { // Named arguments should be accessed without the "a:" prefix in @@ -2563,6 +2570,8 @@ call_user_func( did_emsg |= save_did_emsg; funcdepth_decrement(); + for (i = 0; i < tv_to_free_len; ++i) + clear_tv(tv_to_free[i]); cleanup_function_call(fc); } diff --git a/src/version.c b/src/version.c index 3dee026c1d..6ad07f7d76 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2764, /**/ 2763, /**/ From 0e3ff1919603ee4c4a347fdf761dbdbdeb068015 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 14 Apr 2021 20:35:23 +0200 Subject: [PATCH 27/29] patch 8.2.2765: Vim9: not all blob operations work Problem: Vim9: not all blob operations work. Solution: Run more tests also with Vim9 script and :def functions. Fix what doesn't work. --- src/blob.c | 30 ++++++++++++++++++++++++ src/errors.h | 2 ++ src/eval.c | 16 ++++--------- src/proto/blob.pro | 2 ++ src/testdir/test_blob.vim | 49 ++++++++++++++++++++++++++++++++------- src/testdir/vim9.vim | 49 ++++++++++++++++++++++++++++++++++++++- src/version.c | 2 ++ src/vim9execute.c | 14 +++++++++-- 8 files changed, 140 insertions(+), 24 deletions(-) diff --git a/src/blob.c b/src/blob.c index d758beb19d..f020966ae2 100644 --- a/src/blob.c +++ b/src/blob.c @@ -336,6 +336,36 @@ blob_slice_or_index( return OK; } +/* + * Check if "n1"- is a valid index for a blobl with length "bloblen". + */ + int +check_blob_index(long bloblen, varnumber_T n1, int is_range, int quiet) +{ + if (n1 < 0 || n1 > bloblen) + { + if (!quiet) + semsg(_(e_blobidx), n1); + return FAIL; + } + return OK; +} + +/* + * Check if "n1"-"n2" is a valid range for a blob with length "bloblen". + */ + int +check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet) +{ + if (n2 < 0 || n2 >= bloblen || n2 < n1) + { + if (!quiet) + semsg(_(e_blobidx), n2); + return FAIL; + } + return OK; +} + /* * Set bytes "n1" to "n2" (inclusive) in "dest" to the value of "src". * Caller must make sure "src" is a blob. diff --git a/src/errors.h b/src/errors.h index d38562cfab..99f3aaff5a 100644 --- a/src/errors.h +++ b/src/errors.h @@ -401,3 +401,5 @@ EXTERN char e_cannot_use_underscore_here[] INIT(= N_("E1181: Cannot use an underscore here")); EXTERN char e_blob_required[] INIT(= N_("E1182: Blob required")); +EXTERN char e_cannot_use_range_with_assignment_operator_str[] + INIT(= N_("E1183: Cannot use a range with an assignment operator: %s")); diff --git a/src/eval.c b/src/eval.c index 99768ee280..d8607f2be6 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1175,12 +1175,9 @@ get_lval( lp->ll_n1 = (long)tv_get_number(&var1); clear_tv(&var1); - if (lp->ll_n1 < 0 - || lp->ll_n1 > bloblen - || (lp->ll_range && lp->ll_n1 == bloblen)) + if (check_blob_index(bloblen, lp->ll_n1, lp->ll_range, quiet) + == FAIL) { - if (!quiet) - semsg(_(e_blobidx), lp->ll_n1); clear_tv(&var2); return NULL; } @@ -1188,14 +1185,9 @@ get_lval( { lp->ll_n2 = (long)tv_get_number(&var2); clear_tv(&var2); - if (lp->ll_n2 < 0 - || lp->ll_n2 >= bloblen - || lp->ll_n2 < lp->ll_n1) - { - if (!quiet) - semsg(_(e_blobidx), lp->ll_n2); + if (check_blob_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) + == FAIL) return NULL; - } } lp->ll_blob = lp->ll_tv->vval.v_blob; lp->ll_tv = NULL; diff --git a/src/proto/blob.pro b/src/proto/blob.pro index 7da269c35f..0b7cbd3a86 100644 --- a/src/proto/blob.pro +++ b/src/proto/blob.pro @@ -14,6 +14,8 @@ int write_blob(FILE *fd, blob_T *blob); char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf); blob_T *string2blob(char_u *str); int blob_slice_or_index(blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, int exclusive, typval_T *rettv); +int check_blob_index(long bloblen, varnumber_T n1, int is_range, int quiet); +int check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet); int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src); void blob_remove(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/testdir/test_blob.vim b/src/testdir/test_blob.vim index 34ed0caf2f..3699f3bb19 100644 --- a/src/testdir/test_blob.vim +++ b/src/testdir/test_blob.vim @@ -76,16 +76,47 @@ func Test_blob_assign() END call CheckLegacyAndVim9Success(lines) - " TODO: move to above once it works - let b = 0zDEADBEEF - call assert_fails('let b[2 : 3] = 0z112233', 'E972:') - call assert_fails('let b[2 : 3] = 0z11', 'E972:') - call assert_fails('let b[3 : 2] = 0z', 'E979:') + let lines =<< trim END + VAR b = 0zDEADBEEF + LET b[2 : 3] = 0z112233 + END + call CheckLegacyAndVim9Failure(lines, 'E972:') - call assert_fails('let b ..= 0z33', 'E734:') - call assert_fails('let b ..= "xx"', 'E734:') - call assert_fails('let b += "xx"', 'E734:') - call assert_fails('let b[1 : 1] ..= 0z55', 'E734:') + let lines =<< trim END + VAR b = 0zDEADBEEF + LET b[2 : 3] = 0z11 + END + call CheckLegacyAndVim9Failure(lines, 'E972:') + + let lines =<< trim END + VAR b = 0zDEADBEEF + LET b[3 : 2] = 0z + END + call CheckLegacyAndVim9Failure(lines, 'E979:') + + let lines =<< trim END + VAR b = 0zDEADBEEF + LET b ..= 0z33 + END + call CheckLegacyAndVim9Failure(lines, ['E734:', 'E1019:', 'E734:']) + + let lines =<< trim END + VAR b = 0zDEADBEEF + LET b ..= "xx" + END + call CheckLegacyAndVim9Failure(lines, ['E734:', 'E1019:', 'E734:']) + + let lines =<< trim END + VAR b = 0zDEADBEEF + LET b += "xx" + END + call CheckLegacyAndVim9Failure(lines, ['E734:', 'E1012:', 'E734:']) + + let lines =<< trim END + VAR b = 0zDEADBEEF + LET b[1 : 1] ..= 0z55 + END + call CheckLegacyAndVim9Failure(lines, ['E734:', 'E1183:', 'E734:']) endfunc func Test_blob_get_range() diff --git a/src/testdir/vim9.vim b/src/testdir/vim9.vim index e05f8590bb..9efa65552e 100644 --- a/src/testdir/vim9.vim +++ b/src/testdir/vim9.vim @@ -144,8 +144,23 @@ func CheckLegacySuccess(lines) try exe 'so ' .. fname call Func() - delfunc! Func finally + delfunc! Func + call chdir(cwd) + call delete(fname) + endtry +endfunc + +" Check that "lines" inside a legacy function results in the expected error +func CheckLegacyFailure(lines, error) + let cwd = getcwd() + let fname = 'XlegacyFails' .. s:sequence + let s:sequence += 1 + call writefile(['func Func()'] + a:lines + ['endfunc', 'call Func()'], fname) + try + call assert_fails('so ' .. fname, a:error) + finally + delfunc! Func call chdir(cwd) call delete(fname) endtry @@ -168,3 +183,35 @@ def CheckLegacyAndVim9Success(lines: list) CheckDefSuccess(vim9lines) CheckScriptSuccess(['vim9script'] + vim9lines) enddef + +" Execute "lines" in a legacy function, :def function and Vim9 script. +" Use 'VAR' for a declaration. +" Use 'LET' for an assignment +" Use ' #"' for a comment +def CheckLegacyAndVim9Failure(lines: list, error: any) + var legacyError: string + var defError: string + var scriptError: string + + if type(error) == type('string') + legacyError = error + defError = error + scriptError = error + else + legacyError = error[0] + defError = error[1] + scriptError = error[2] + endif + + var legacylines = lines->mapnew((_, v) => + v->substitute('\', 'let', 'g') + ->substitute('\', 'let', 'g') + ->substitute('#"', ' "', 'g')) + CheckLegacyFailure(legacylines, legacyError) + + var vim9lines = lines->mapnew((_, v) => + v->substitute('\', 'var', 'g') + ->substitute('\vval.v_blob, - n1, n2, tv); + { + long bloblen = blob_len(tv_dest->vval.v_blob); + + if (check_blob_index(bloblen, + n1, TRUE, FALSE) == FAIL + || check_blob_range(bloblen, + n1, n2, FALSE) == FAIL) + status = FAIL; + else + status = blob_set_range( + tv_dest->vval.v_blob, n1, n2, tv); + } } } From b1419268901b194b295b7cea18a1d0968f9dddfc Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 14 Apr 2021 20:54:07 +0200 Subject: [PATCH 28/29] patch 8.2.2766: test failure Problem: Test failure. Solution: Add change to Vim9 compilation error message. --- src/version.c | 2 ++ src/vim9compile.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/version.c b/src/version.c index 0127c2c51c..cdf37d86f0 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2766, /**/ 2765, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 0ada441a4b..6286114055 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -6472,7 +6472,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) goto theend; if (range) { - semsg(_(e_cannot_use_range_with_assignment_str), + semsg(_(e_cannot_use_range_with_assignment_operator_str), var_start); return FAIL; } From bd6406f15db210b78fa24dece3bd021a7ac085dc Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 14 Apr 2021 21:30:06 +0200 Subject: [PATCH 29/29] patch 8.2.2767: compiler warning for unused argument Problem: Compiler warning for unused argument. Solution: Remove the argument. --- src/blob.c | 2 +- src/eval.c | 3 +-- src/proto/blob.pro | 2 +- src/version.c | 2 ++ src/vim9execute.c | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/blob.c b/src/blob.c index f020966ae2..114dacdec2 100644 --- a/src/blob.c +++ b/src/blob.c @@ -340,7 +340,7 @@ blob_slice_or_index( * Check if "n1"- is a valid index for a blobl with length "bloblen". */ int -check_blob_index(long bloblen, varnumber_T n1, int is_range, int quiet) +check_blob_index(long bloblen, varnumber_T n1, int quiet) { if (n1 < 0 || n1 > bloblen) { diff --git a/src/eval.c b/src/eval.c index d8607f2be6..f8e922f911 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1175,8 +1175,7 @@ get_lval( lp->ll_n1 = (long)tv_get_number(&var1); clear_tv(&var1); - if (check_blob_index(bloblen, lp->ll_n1, lp->ll_range, quiet) - == FAIL) + if (check_blob_index(bloblen, lp->ll_n1, quiet) == FAIL) { clear_tv(&var2); return NULL; diff --git a/src/proto/blob.pro b/src/proto/blob.pro index 0b7cbd3a86..3adaf0ffe5 100644 --- a/src/proto/blob.pro +++ b/src/proto/blob.pro @@ -14,7 +14,7 @@ int write_blob(FILE *fd, blob_T *blob); char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf); blob_T *string2blob(char_u *str); int blob_slice_or_index(blob_T *blob, int is_range, varnumber_T n1, varnumber_T n2, int exclusive, typval_T *rettv); -int check_blob_index(long bloblen, varnumber_T n1, int is_range, int quiet); +int check_blob_index(long bloblen, varnumber_T n1, int quiet); int check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet); int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src); void blob_remove(typval_T *argvars, typval_T *rettv); diff --git a/src/version.c b/src/version.c index cdf37d86f0..085a182ed7 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2767, /**/ 2766, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index bdf4a02b71..b7db0e2944 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2282,7 +2282,7 @@ call_def_function( long bloblen = blob_len(tv_dest->vval.v_blob); if (check_blob_index(bloblen, - n1, TRUE, FALSE) == FAIL + n1, FALSE) == FAIL || check_blob_range(bloblen, n1, n2, FALSE) == FAIL) status = FAIL;