# HG changeset patch # User Bram Moolenaar # Date 1621203303 -7200 # Node ID 668df21d8bc61c5047f0f1eeeb123889c4d175c1 # Parent 20eabe0f4d72a09193b9349f691f12a14ca7366a patch 8.2.2861: Vim9: "legacy return" is not recognized as a return statement Commit: https://github.com/vim/vim/commit/3b1373b193ce5fbf25e852277a4ecc98688c7bb8 Author: Bram Moolenaar Date: Mon May 17 00:01:42 2021 +0200 patch 8.2.2861: Vim9: "legacy return" is not recognized as a return statement Problem: Vim9: "legacy return" is not recognized as a return statement. Solution: Specifically check for a return command. (closes https://github.com/vim/vim/issues/8213) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -2777,6 +2777,10 @@ def Test_expr7_negate_add() CheckDefAndScriptFailure(lines, 'E15:') enddef +def LegacyReturn(): string + legacy return #{key: 'ok'}.key +enddef + def Test_expr7_legacy_script() var lines =<< trim END let s:legacy = 'legacy' @@ -2790,6 +2794,17 @@ def Test_expr7_legacy_script() call assert_equal('legacy', GetLocalPrefix()) END CheckScriptSuccess(lines) + + assert_equal('ok', LegacyReturn()) + + lines =<< trim END + vim9script + def GetNumber(): number + legacy return range(3)->map('v:val + 1') + enddef + echo GetNumber() + END + CheckScriptFailure(lines, 'E1012: Type mismatch; expected number but got list') enddef def Echo(arg: any): string diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2861, +/**/ 2860, /**/ 2859, diff --git a/src/vim9.h b/src/vim9.h --- a/src/vim9.h +++ b/src/vim9.h @@ -14,6 +14,7 @@ typedef enum { ISN_EXEC, // execute Ex command line isn_arg.string ISN_EXECCONCAT, // execute Ex command from isn_arg.number items on stack + ISN_LEGACY_EVAL, // evaluate expression isn_arg.string with legacy syntax. ISN_ECHO, // echo isn_arg.echo.echo_count items on top of stack ISN_EXECUTE, // execute Ex commands isn_arg.number items on top of stack ISN_ECHOMSG, // echo Ex commands isn_arg.number items on top of stack diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2174,6 +2174,25 @@ generate_EXEC(cctx_T *cctx, char_u *line } static int +generate_LEGACY_EVAL(cctx_T *cctx, char_u *line) +{ + isn_T *isn; + garray_T *stack = &cctx->ctx_type_stack; + + RETURN_OK_IF_SKIP(cctx); + if ((isn = generate_instr(cctx, ISN_LEGACY_EVAL)) == NULL) + return FAIL; + isn->isn_arg.string = vim_strsave(line); + + if (ga_grow(stack, 1) == FAIL) + return FAIL; + ((type_T **)stack->ga_data)[stack->ga_len] = &t_any; + ++stack->ga_len; + + return OK; +} + + static int generate_EXECCONCAT(cctx_T *cctx, int count) { isn_T *isn; @@ -5321,10 +5340,11 @@ compile_expr0(char_u **arg, cctx_T *cct } /* - * compile "return [expr]" + * Compile "return [expr]". + * When "legacy" is TRUE evaluate [expr] with legacy syntax */ static char_u * -compile_return(char_u *arg, int check_return_type, cctx_T *cctx) +compile_return(char_u *arg, int check_return_type, int legacy, cctx_T *cctx) { char_u *p = arg; garray_T *stack = &cctx->ctx_type_stack; @@ -5332,9 +5352,24 @@ compile_return(char_u *arg, int check_re if (*p != NUL && *p != '|' && *p != '\n') { - // compile return argument into instructions - if (compile_expr0(&p, cctx) == FAIL) - return NULL; + if (legacy) + { + int save_flags = cmdmod.cmod_flags; + + generate_LEGACY_EVAL(cctx, p); + if (need_type(&t_any, cctx->ctx_ufunc->uf_ret_type, -1, + 0, cctx, FALSE, FALSE) == FAIL) + return NULL; + cmdmod.cmod_flags |= CMOD_LEGACY; + (void)skip_expr(&p, NULL); + cmdmod.cmod_flags = save_flags; + } + else + { + // compile return argument into instructions + if (compile_expr0(&p, cctx) == FAIL) + return NULL; + } if (cctx->ctx_skip != SKIP_YES) { @@ -9193,7 +9228,15 @@ compile_def_function( // When using ":legacy cmd" always use compile_exec(). if (local_cmdmod.cmod_flags & CMOD_LEGACY) - ea.cmdidx = CMD_legacy; + { + char_u *start = ea.cmd; + + // ":legacy return expr" needs to be handled differently. + if (checkforcmd(&start, "return", 4)) + ea.cmdidx = CMD_return; + else + ea.cmdidx = CMD_legacy; + } if (p == ea.cmd && ea.cmdidx != CMD_SIZE) { @@ -9254,7 +9297,8 @@ compile_def_function( goto erret; case CMD_return: - line = compile_return(p, check_return_type, &cctx); + line = compile_return(p, check_return_type, + local_cmdmod.cmod_flags & CMOD_LEGACY, &cctx); cctx.ctx_had_return = TRUE; break; @@ -9605,6 +9649,7 @@ delete_instr(isn_T *isn) { case ISN_DEF: case ISN_EXEC: + case ISN_LEGACY_EVAL: case ISN_LOADAUTO: case ISN_LOADB: case ISN_LOADENV: diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1388,6 +1388,27 @@ exec_instructions(ectx_T *ectx) } break; + // Evaluate an expression with legacy syntax, push it onto the + // stack. + case ISN_LEGACY_EVAL: + { + char_u *arg = iptr->isn_arg.string; + int res; + int save_flags = cmdmod.cmod_flags; + + if (GA_GROW(&ectx->ec_stack, 1) == FAIL) + return FAIL; + tv = STACK_TV_BOT(0); + init_tv(tv); + cmdmod.cmod_flags |= CMOD_LEGACY; + res = eval0(arg, tv, NULL, &EVALARG_EVALUATE); + cmdmod.cmod_flags = save_flags; + if (res == FAIL) + goto on_error; + ++ectx->ec_stack.ga_len; + } + break; + // push typeval VAR_INSTR with instructions to be executed case ISN_INSTR: { @@ -4464,6 +4485,10 @@ list_instructions(char *pfx, isn_T *inst case ISN_EXEC: smsg("%s%4d EXEC %s", pfx, current, iptr->isn_arg.string); break; + case ISN_LEGACY_EVAL: + smsg("%s%4d EVAL legacy %s", pfx, current, + iptr->isn_arg.string); + break; case ISN_REDIRSTART: smsg("%s%4d REDIR", pfx, current); break;