# HG changeset patch # User Bram Moolenaar # Date 1580760004 -3600 # Node ID 86665583dc83a6b5587530cd83e1d282259e2e6f # Parent 938f4994ad87928963d73ccc19ade357f9462443 patch 8.2.0201: cannot assign to an imported variable Commit: https://github.com/vim/vim/commit/4e12a5df37ed4a47ca94a8b9393c768e8ca1f75a Author: Bram Moolenaar Date: Mon Feb 3 20:50:59 2020 +0100 patch 8.2.0201: cannot assign to an imported variable Problem: Cannot assign to an imported variable. Solution: Make it work. diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -2296,7 +2296,7 @@ get_var_tv( if (tv == NULL && current_sctx.sc_version == SCRIPT_VERSION_VIM9) { - imported_T *import = find_imported(name, NULL); + imported_T *import = find_imported(name, 0, NULL); // imported variable from another script if (import != NULL) @@ -2472,7 +2472,7 @@ lookup_scriptvar(char_u *name, size_t le res = HASHITEM_EMPTY(hi) ? -1 : 1; // if not script-local, then perhaps imported - if (res == -1 && find_imported(p, NULL) != NULL) + if (res == -1 && find_imported(p, 0, NULL) != NULL) res = 1; if (p != buffer) diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -4,7 +4,7 @@ type_T *parse_type(char_u **arg, garray_ char *vartype_name(vartype_T type); char *type_name(type_T *type, char **tofree); int get_script_item_idx(int sid, char_u *name, int check_writable); -imported_T *find_imported(char_u *name, cctx_T *cctx); +imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx); char_u *to_name_end(char_u *arg); char_u *to_name_const_end(char_u *arg); int assignment_len(char_u *p, int *heredoc); diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -320,9 +320,11 @@ def Test_import_absolute() \ 'import exported from "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim"', \ 'def UseExported()', \ ' g:imported_abs = exported', + \ ' exported = 8888', + \ ' g:imported_after = exported', \ 'enddef', \ 'UseExported()', - \ 'g:import_disassabled = execute("disass UseExported")', + \ 'g:import_disassembled = execute("disass UseExported")', \ ] writefile(import_lines, 'Ximport_abs.vim') writefile(s:export_script_lines, 'Xexport_abs.vim') @@ -330,12 +332,19 @@ def Test_import_absolute() source Ximport_abs.vim assert_equal(9876, g:imported_abs) + assert_equal(8888, g:imported_after) assert_match('\d\+_UseExported.*' \ .. 'g:imported_abs = exported.*' \ .. '0 LOADSCRIPT exported from .*Xexport_abs.vim.*' - \ .. '1 STOREG g:imported_abs', g:import_disassabled) + \ .. '1 STOREG g:imported_abs.*' + \ .. 'exported = 8888.*' + \ .. '3 STORESCRIPT exported in .*Xexport_abs.vim.*' + \ .. 'g:imported_after = exported.*' + \ .. '4 LOADSCRIPT exported from .*Xexport_abs.vim.*' + \ .. '5 STOREG g:imported_after.*' + \, g:import_disassembled) unlet g:imported_abs - unlet g:import_disassabled + unlet g:import_disassembled delete('Ximport_abs.vim') delete('Xexport_abs.vim') diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -678,7 +678,7 @@ find_func_even_dead(char_u *name, cctx_T return func; // Find imported funcion before global one. - imported = find_imported(name, cctx); + imported = find_imported(name, 0, cctx); if (imported != NULL && imported->imp_funcname != NULL) { hi = hash_find(&func_hashtab, imported->imp_funcname); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -743,6 +743,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 201, +/**/ 200, /**/ 199, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1467,7 +1467,7 @@ get_script_item_idx(int sid, char_u *nam * Find "name" in imported items of the current script/ */ imported_T * -find_imported(char_u *name, cctx_T *cctx) +find_imported(char_u *name, size_t len, cctx_T *cctx) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); int idx; @@ -1478,7 +1478,9 @@ find_imported(char_u *name, cctx_T *cctx imported_T *import = ((imported_T *)cctx->ctx_imports.ga_data) + idx; - if (STRCMP(name, import->imp_name) == 0) + if (len == 0 ? STRCMP(name, import->imp_name) == 0 + : STRLEN(import->imp_name) == len + && STRNCMP(name, import->imp_name, len) == 0) return import; } @@ -1486,7 +1488,9 @@ find_imported(char_u *name, cctx_T *cctx { imported_T *import = ((imported_T *)si->sn_imports.ga_data) + idx; - if (STRCMP(name, import->imp_name) == 0) + if (len == 0 ? STRCMP(name, import->imp_name) == 0 + : STRLEN(import->imp_name) == len + && STRNCMP(name, import->imp_name, len) == 0) return import; } return NULL; @@ -1517,7 +1521,7 @@ compile_load_scriptvar(cctx_T *cctx, cha return OK; } - import = find_imported(name, cctx); + import = find_imported(name, 0, cctx); if (import != NULL) { // TODO: check this is a variable, not a function @@ -3071,6 +3075,16 @@ heredoc_getline( [cctx->ctx_lnum]); } +typedef enum { + dest_local, + dest_option, + dest_env, + dest_global, + dest_vimvar, + dest_script, + dest_reg, +} assign_dest_T; + /* * compile "let var [= expr]", "const var = expr" and "var = expr" * "arg" points to "var". @@ -3086,14 +3100,10 @@ compile_assignment(char_u *arg, exarg_T garray_T *instr = &cctx->ctx_instr; int idx = -1; char_u *op; - int option = FALSE; int opt_type; + assign_dest_T dest = dest_local; int opt_flags = 0; - int global = FALSE; - int env = FALSE; - int reg = FALSE; int vimvaridx = -1; - int script = FALSE; int oplen = 0; int heredoc = FALSE; type_T *type; @@ -3125,7 +3135,7 @@ compile_assignment(char_u *arg, exarg_T long numval; char_u *stringval = NULL; - option = TRUE; + dest = dest_option; if (cmdidx == CMD_const) { emsg(_(e_const_option)); @@ -3159,7 +3169,7 @@ compile_assignment(char_u *arg, exarg_T } else if (*arg == '$') { - env = TRUE; + dest = dest_env; if (is_decl) { semsg(_("E1065: Cannot declare an environment variable: %s"), name); @@ -3173,7 +3183,7 @@ compile_assignment(char_u *arg, exarg_T emsg_invreg(arg[1]); return FAIL; } - reg = TRUE; + dest = dest_reg; if (is_decl) { semsg(_("E1066: Cannot declare a register: %s"), name); @@ -3182,7 +3192,7 @@ compile_assignment(char_u *arg, exarg_T } else if (STRNCMP(arg, "g:", 2) == 0) { - global = TRUE; + dest = dest_global; if (is_decl) { semsg(_("E1016: Cannot declare a global variable: %s"), name); @@ -3197,6 +3207,7 @@ compile_assignment(char_u *arg, exarg_T semsg(_(e_var_notfound), arg); goto theend; } + dest = dest_vimvar; if (is_decl) { semsg(_("E1064: Cannot declare a v: variable: %s"), name); @@ -3232,9 +3243,10 @@ compile_assignment(char_u *arg, exarg_T } else if ((STRNCMP(arg, "s:", 2) == 0 ? lookup_script(arg + 2, varlen - 2) - : lookup_script(arg, varlen)) == OK) + : lookup_script(arg, varlen)) == OK + || find_imported(arg, varlen, cctx) != NULL) { - script = TRUE; + dest = dest_script; if (is_decl) { semsg(_("E1054: Variable already declared in the script: %s"), @@ -3244,7 +3256,7 @@ compile_assignment(char_u *arg, exarg_T } } - if (!option) + if (dest != dest_option) { if (is_decl && *p == ':') { @@ -3279,15 +3291,15 @@ compile_assignment(char_u *arg, exarg_T semsg(_(e_white_both), buf); } - if (oplen == 3 && !heredoc && !global && type->tt_type != VAR_STRING - && type->tt_type != VAR_UNKNOWN) + if (oplen == 3 && !heredoc && dest != dest_global + && type->tt_type != VAR_STRING && type->tt_type != VAR_UNKNOWN) { emsg("E1019: Can only concatenate to string"); goto theend; } // +=, /=, etc. require an existing variable - if (idx < 0 && !global && !env && !reg && !option) + if (idx < 0 && dest == dest_local) { if (oplen > 1 && !heredoc) { @@ -3328,20 +3340,32 @@ compile_assignment(char_u *arg, exarg_T // for "+=", "*=", "..=" etc. first load the current value if (*op != '=') { - if (option) - // TODO: check the option exists - generate_LOAD(cctx, ISN_LOADOPT, 0, name + 1, type); - else if (global) - generate_LOAD(cctx, ISN_LOADG, 0, name + 2, type); - else if (env) - // Include $ in the name here - generate_LOAD(cctx, ISN_LOADENV, 0, name, type); - else if (reg) - generate_LOAD(cctx, ISN_LOADREG, arg[1], NULL, &t_string); - else if (vimvaridx >= 0) - generate_LOADV(cctx, name + 2, TRUE); - else - generate_LOAD(cctx, ISN_LOAD, idx, NULL, type); + switch (dest) + { + case dest_option: + // TODO: check the option exists + generate_LOAD(cctx, ISN_LOADOPT, 0, name + 1, type); + break; + case dest_global: + generate_LOAD(cctx, ISN_LOADG, 0, name + 2, type); + break; + case dest_script: + compile_load_scriptvar(cctx, name); + break; + case dest_env: + // Include $ in the name here + generate_LOAD(cctx, ISN_LOADENV, 0, name, type); + break; + case dest_reg: + generate_LOAD(cctx, ISN_LOADREG, arg[1], NULL, &t_string); + break; + case dest_vimvar: + generate_LOADV(cctx, name + 2, TRUE); + break; + case dest_local: + generate_LOAD(cctx, ISN_LOAD, idx, NULL, type); + break; + } } // compile the expression @@ -3377,7 +3401,7 @@ compile_assignment(char_u *arg, exarg_T emsg(_("E1021: const requires a value")); goto theend; } - else if (!has_type || option) + else if (!has_type || dest == dest_option) { emsg(_("E1022: type or initialization required")); goto theend; @@ -3427,49 +3451,68 @@ compile_assignment(char_u *arg, exarg_T } } - if (option) - generate_STOREOPT(cctx, name + 1, opt_flags); - else if (global) - // include g: with the name, easier to execute that way - generate_STORE(cctx, ISN_STOREG, 0, name); - else if (env) - generate_STORE(cctx, ISN_STOREENV, 0, name + 1); - else if (reg) - generate_STORE(cctx, ISN_STOREREG, name[1], NULL); - else if (vimvaridx >= 0) - generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL); - else if (script) + switch (dest) { - char_u *rawname = name + (name[1] == ':' ? 2 : 0); - - idx = get_script_item_idx(current_sctx.sc_sid, rawname, TRUE); - // TODO: specific type - if (idx < 0) - generate_OLDSCRIPT(cctx, ISN_STORES, rawname, - current_sctx.sc_sid, &t_any); - else - generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT, - current_sctx.sc_sid, idx, &t_any); - } - else - { - isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1; - - // optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE into - // ISN_STORENR - if (instr->ga_len == instr_count + 1 && isn->isn_type == ISN_PUSHNR) - { - varnumber_T val = isn->isn_arg.number; - garray_T *stack = &cctx->ctx_type_stack; - - isn->isn_type = ISN_STORENR; - isn->isn_arg.storenr.str_idx = idx; - isn->isn_arg.storenr.str_val = val; - if (stack->ga_len > 0) - --stack->ga_len; - } - else - generate_STORE(cctx, ISN_STORE, idx, NULL); + case dest_option: + generate_STOREOPT(cctx, name + 1, opt_flags); + break; + case dest_global: + // include g: with the name, easier to execute that way + generate_STORE(cctx, ISN_STOREG, 0, name); + break; + case dest_env: + generate_STORE(cctx, ISN_STOREENV, 0, name + 1); + break; + case dest_reg: + generate_STORE(cctx, ISN_STOREREG, name[1], NULL); + break; + case dest_vimvar: + generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL); + break; + case dest_script: + { + char_u *rawname = name + (name[1] == ':' ? 2 : 0); + imported_T *import = NULL; + int sid = current_sctx.sc_sid; + + if (name[1] != ':') + { + import = find_imported(name, 0, cctx); + if (import != NULL) + sid = import->imp_sid; + } + + idx = get_script_item_idx(sid, rawname, TRUE); + // TODO: specific type + if (idx < 0) + generate_OLDSCRIPT(cctx, ISN_STORES, rawname, sid, &t_any); + else + generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT, + sid, idx, &t_any); + } + break; + case dest_local: + { + isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1; + + // optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE + // into ISN_STORENR + if (instr->ga_len == instr_count + 1 + && isn->isn_type == ISN_PUSHNR) + { + varnumber_T val = isn->isn_arg.number; + garray_T *stack = &cctx->ctx_type_stack; + + isn->isn_type = ISN_STORENR; + isn->isn_arg.storenr.str_idx = idx; + isn->isn_arg.storenr.str_val = val; + if (stack->ga_len > 0) + --stack->ga_len; + } + else + generate_STORE(cctx, ISN_STORE, idx, NULL); + } + break; } ret = p; @@ -4619,7 +4662,6 @@ compile_def_function(ufunc_T *ufunc, int // "funcname(" is always a function call. // "varname[]" is an expression. - // "g:varname" is an expression. // "varname->expr" is an expression. if (*p == '(' || *p == '[' @@ -4643,7 +4685,8 @@ compile_def_function(ufunc_T *ufunc, int || *ea.cmd == '@' || ((p - ea.cmd) > 2 && ea.cmd[1] == ':') || lookup_local(ea.cmd, p - ea.cmd, &cctx) >= 0 - || lookup_script(ea.cmd, p - ea.cmd) == OK) + || lookup_script(ea.cmd, p - ea.cmd) == OK + || find_imported(ea.cmd, p - ea.cmd, &cctx) != NULL) { line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx); if (line == NULL)