]> granicus.if.org Git - vim/commitdiff
patch 8.2.0201: cannot assign to an imported variable v8.2.0201
authorBram Moolenaar <Bram@vim.org>
Mon, 3 Feb 2020 19:50:59 +0000 (20:50 +0100)
committerBram Moolenaar <Bram@vim.org>
Mon, 3 Feb 2020 19:50:59 +0000 (20:50 +0100)
Problem:    Cannot assign to an imported variable.
Solution:   Make it work.

src/evalvars.c
src/proto/vim9compile.pro
src/testdir/test_vim9_script.vim
src/userfunc.c
src/version.c
src/vim9compile.c

index 32c803fd9fb47096eb694ab9d426325017174a08..2bbc3553b684c595091b4ed4f09e3c5528efc66a 100644 (file)
@@ -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 len, cctx_T *dummy UNUSED)
     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)
index 4f06e9d9cea1a50b101bb5e2a984b8cd7ccf3bdc..73cf13f49c9d5d632db397b6cdffe745d77a6464 100644 (file)
@@ -4,7 +4,7 @@ type_T *parse_type(char_u **arg, garray_T *type_list);
 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);
index 023fc0f751d64ebc918d6cf0fa88a9de690819a3..2eeea5455032ad54feafdaebb1c37d54f6d1474b 100644 (file)
@@ -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('<SNR>\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')
index fd4d1ce5b1f1f05a2a612a7174703b9f321ddb4c..2904ee986acbd6aacb66d9be88a0cb4b867a9fb0 100644 (file)
@@ -678,7 +678,7 @@ find_func_even_dead(char_u *name, cctx_T *cctx)
            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);
index 6b7fe09526ee8ef432d74c769fb4537a464c2ce3..01e54a03345f0a6691c57bb6f15cb33b4a64002e 100644 (file)
@@ -742,6 +742,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    201,
 /**/
     200,
 /**/
index 570c6a494830490fdf5d8487d7cad7d24eef7d81..40411c4d3a8c080fdd04b88be641b79387bedd9a 100644 (file)
@@ -1467,7 +1467,7 @@ get_script_item_idx(int sid, char_u *name, int check_writable)
  * 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, char_u *name)
        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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
     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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
        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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
     }
     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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
            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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
     }
     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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
            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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
        }
        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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
        }
     }
 
-    if (!option)
+    if (dest != dest_option)
     {
        if (is_decl && *p == ':')
        {
@@ -3279,15 +3291,15 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
        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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
        // 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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
        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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
        }
     }
 
-    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);
+       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;
 
-       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;
+               if (name[1] != ':')
+               {
+                   import = find_imported(name, 0, cctx);
+                   if (import != NULL)
+                       sid = import->imp_sid;
+               }
 
-       // 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;
+               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;
 
-           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);
+               // 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 set_return_type)
 
            // "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 set_return_type)
                        || *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)