# HG changeset patch # User Bram Moolenaar # Date 1588538703 -7200 # Node ID 350bb78345bab47ece643b0a8c1e9a8c576726bf # Parent ffb9ba06903257d96a4f2918aa4825cf2356f233 patch 8.2.0693: closure using argument not tested Commit: https://github.com/vim/vim/commit/2fd4cd755c3e87e733b7363ac13e5c0fe0297a80 Author: Bram Moolenaar Date: Sun May 3 22:30:49 2020 +0200 patch 8.2.0693: closure using argument not tested Problem: Closure using argument not tested. Solution: Add a test, make it work. diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -700,5 +700,26 @@ def Test_closure_two_indirect_refs() unlet g:Read enddef +def MakeArgRefs(theArg: string) + let local = 'loc_val' + g:UseArg = {s -> theArg .. '/' .. local .. '/' .. s} +enddef + +def MakeArgRefsVarargs(theArg: string, ...rest: list) + let local = 'the_loc' + g:UseVararg = {s -> theArg .. '/' .. local .. '/' .. s .. '/' .. join(rest)} +enddef + +def Test_closure_using_argument() + MakeArgRefs('arg_val') + assert_equal('arg_val/loc_val/call_val', g:UseArg('call_val')) + + MakeArgRefsVarargs('arg_val', 'one', 'two') + assert_equal('arg_val/the_loc/call_val/one two', g:UseVararg('call_val')) + + unlet g:UseArg + unlet g:UseVararg +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 693, +/**/ 692, /**/ 691, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -186,37 +186,74 @@ lookup_local(char_u *name, size_t len, c } /* - * Lookup an argument in the current function. - * Returns the argument index or -1 if not found. + * Lookup an argument in the current function and an enclosing function. + * Returns the argument index in "idxp" + * Returns the argument type in "type" + * Sets "gen_load_outer" to TRUE if found in outer scope. + * Returns OK when found, FAIL otherwise. */ static int -lookup_arg(char_u *name, size_t len, cctx_T *cctx) +lookup_arg( + char_u *name, + size_t len, + int *idxp, + type_T **type, + int *gen_load_outer, + cctx_T *cctx) { int idx; + char_u *va_name; if (len == 0) - return -1; + return FAIL; for (idx = 0; idx < cctx->ctx_ufunc->uf_args.ga_len; ++idx) { char_u *arg = FUNCARG(cctx->ctx_ufunc, idx); - if (STRNCMP(name, arg, len) == 0 && STRLEN(arg) == len) - return idx; - } - return -1; -} - -/* - * Lookup a vararg argument in the current function. - * Returns TRUE if there is a match. - */ - static int -lookup_vararg(char_u *name, size_t len, cctx_T *cctx) -{ - char_u *va_name = cctx->ctx_ufunc->uf_va_name; - - return len > 0 && va_name != NULL - && STRNCMP(name, va_name, len) == 0 && STRLEN(va_name) == len; + if (STRNCMP(name, arg, len) == 0 && arg[len] == NUL) + { + if (idxp != NULL) + { + // Arguments are located above the frame pointer. One further + // if there is a vararg argument + *idxp = idx - (cctx->ctx_ufunc->uf_args.ga_len + + STACK_FRAME_SIZE) + + (cctx->ctx_ufunc->uf_va_name != NULL ? -1 : 0); + + if (cctx->ctx_ufunc->uf_arg_types != NULL) + *type = cctx->ctx_ufunc->uf_arg_types[idx]; + else + *type = &t_any; + } + return OK; + } + } + + va_name = cctx->ctx_ufunc->uf_va_name; + if (va_name != NULL + && STRNCMP(name, va_name, len) == 0 && va_name[len] == NUL) + { + if (idxp != NULL) + { + // varargs is always the last argument + *idxp = -STACK_FRAME_SIZE - 1; + *type = cctx->ctx_ufunc->uf_va_type; + } + return OK; + } + + if (cctx->ctx_outer != NULL) + { + // Lookup the name for an argument of the outer function. + if (lookup_arg(name, len, idxp, type, gen_load_outer, cctx->ctx_outer) + == OK) + { + *gen_load_outer = TRUE; + return OK; + } + } + + return FAIL; } /* @@ -1584,7 +1621,7 @@ reserve_local(cctx_T *cctx, char_u *name { lvar_T *lvar; - if (lookup_arg(name, len, cctx) >= 0 || lookup_vararg(name, len, cctx)) + if (lookup_arg(name, len, NULL, NULL, NULL, cctx) == OK) { emsg_namelen(_("E1006: %s is used as an argument"), name, (int)len); return NULL; @@ -2452,26 +2489,10 @@ compile_load(char_u **arg, char_u *end_a if (name == NULL) return FAIL; - idx = lookup_arg(*arg, len, cctx); - if (idx >= 0) + if (lookup_arg(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK) { - if (cctx->ctx_ufunc->uf_arg_types != NULL) - type = cctx->ctx_ufunc->uf_arg_types[idx]; - else - type = &t_any; - - // Arguments are located above the frame pointer. - idx -= cctx->ctx_ufunc->uf_args.ga_len + STACK_FRAME_SIZE; - if (cctx->ctx_ufunc->uf_va_name != NULL) - --idx; - gen_load = TRUE; - } - else if (lookup_vararg(*arg, len, cctx)) - { - // varargs is always the last argument - idx = -STACK_FRAME_SIZE - 1; - type = cctx->ctx_ufunc->uf_va_type; - gen_load = TRUE; + if (!gen_load_outer) + gen_load = TRUE; } else {