Patch 8.2.1824
Problem:    Vim9: variables at the script level escape their scope.
Solution:   When leaving a scope remove variables declared in it.
Files:      src/structs.h, src/ex_eval.c, src/evalvars.c,
            src/proto/evalvars.pro, src/testdir/test_vim9_script.vim


*** ../vim-8.2.1823/src/structs.h       2020-10-10 14:12:58.024646147 +0200
--- src/structs.h       2020-10-10 17:27:40.236527958 +0200
***************
*** 889,894 ****
--- 889,896 ----
      }         cs_pend;
      void      *cs_forinfo[CSTACK_LEN]; // info used by ":for"
      int               cs_line[CSTACK_LEN];    // line nr of ":while"/":for" 
line
+     int               cs_script_var_len[CSTACK_LEN];  // value of 
sn_var_vals.ga_len
+                                               // when entering the block
      int               cs_idx;                 // current entry, or -1 if none
      int               cs_looplevel;           // nr of nested ":while"s and 
":for"s
      int               cs_trylevel;            // nr of nested ":try"s
*** ../vim-8.2.1823/src/ex_eval.c       2020-09-10 19:25:01.612194701 +0200
--- src/ex_eval.c       2020-10-10 18:27:24.558010168 +0200
***************
*** 906,911 ****
--- 906,953 ----
  }
  
  /*
+  * Start a new scope/block.  Caller should have checked that cs_idx is not
+  * exceeding CSTACK_LEN.
+  */
+     static void
+ enter_block(cstack_T *cstack)
+ {
+     ++cstack->cs_idx;
+     if (in_vim9script())
+       cstack->cs_script_var_len[cstack->cs_idx] =
+                         SCRIPT_ITEM(current_sctx.sc_sid)->sn_var_vals.ga_len;
+ }
+ 
+     static void
+ leave_block(cstack_T *cstack)
+ {
+     int i;
+ 
+     if (in_vim9script())
+     {
+       scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
+ 
+       for (i = cstack->cs_script_var_len[cstack->cs_idx];
+                                              i < si->sn_var_vals.ga_len; ++i)
+       {
+           svar_T      *sv = ((svar_T *)si->sn_var_vals.ga_data) + i;
+           hashtab_T   *ht = get_script_local_ht();
+           hashitem_T  *hi;
+ 
+           if (ht != NULL)
+           {
+               // Remove a variable declared inside the block, if it still
+               // exists.
+               hi = hash_find(ht, sv->sv_name);
+               if (!HASHITEM_EMPTY(hi))
+                   delete_var(ht, hi);
+           }
+       }
+     }
+     --cstack->cs_idx;
+ }
+ 
+ /*
   * ":if".
   */
      void
***************
*** 920,931 ****
        eap->errmsg = _("E579: :if nesting too deep");
      else
      {
!       ++cstack->cs_idx;
        cstack->cs_flags[cstack->cs_idx] = 0;
  
        /*
!        * Don't do something after an error, interrupt, or throw, or when there
!        * is a surrounding conditional and it was not active.
         */
        skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
                && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE));
--- 962,973 ----
        eap->errmsg = _("E579: :if nesting too deep");
      else
      {
!       enter_block(cstack);
        cstack->cs_flags[cstack->cs_idx] = 0;
  
        /*
!        * Don't do something after an error, interrupt, or throw, or when
!        * there is a surrounding conditional and it was not active.
         */
        skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
                && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE));
***************
*** 949,957 ****
      void
  ex_endif(exarg_T *eap)
  {
      did_endif = TRUE;
!     if (eap->cstack->cs_idx < 0
!           || (eap->cstack->cs_flags[eap->cstack->cs_idx]
                                           & (CSF_WHILE | CSF_FOR | CSF_TRY)))
        eap->errmsg = _(e_endif_without_if);
      else
--- 991,1001 ----
      void
  ex_endif(exarg_T *eap)
  {
+     cstack_T  *cstack = eap->cstack;
+ 
      did_endif = TRUE;
!     if (cstack->cs_idx < 0
!           || (cstack->cs_flags[cstack->cs_idx]
                                           & (CSF_WHILE | CSF_FOR | CSF_TRY)))
        eap->errmsg = _(e_endif_without_if);
      else
***************
*** 965,975 ****
         * Doing this here prevents an exception for a parsing error being
         * discarded by throwing the interrupt exception later on.
         */
!       if (!(eap->cstack->cs_flags[eap->cstack->cs_idx] & CSF_TRUE)
                                                    && dbg_check_skipped(eap))
!           (void)do_intthrow(eap->cstack);
  
!       --eap->cstack->cs_idx;
      }
  }
  
--- 1009,1019 ----
         * Doing this here prevents an exception for a parsing error being
         * discarded by throwing the interrupt exception later on.
         */
!       if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE)
                                                    && dbg_check_skipped(eap))
!           (void)do_intthrow(cstack);
  
!       leave_block(cstack);
      }
  }
  
***************
*** 1086,1092 ****
         */
        if ((cstack->cs_lflags & CSL_HAD_LOOP) == 0)
        {
!           ++cstack->cs_idx;
            ++cstack->cs_looplevel;
            cstack->cs_line[cstack->cs_idx] = -1;
        }
--- 1130,1136 ----
         */
        if ((cstack->cs_lflags & CSL_HAD_LOOP) == 0)
        {
!           enter_block(cstack);
            ++cstack->cs_looplevel;
            cstack->cs_line[cstack->cs_idx] = -1;
        }
***************
*** 1450,1456 ****
        eap->errmsg = _("E601: :try nesting too deep");
      else
      {
!       ++cstack->cs_idx;
        ++cstack->cs_trylevel;
        cstack->cs_flags[cstack->cs_idx] = CSF_TRY;
        cstack->cs_pending[cstack->cs_idx] = CSTP_NONE;
--- 1494,1500 ----
        eap->errmsg = _("E601: :try nesting too deep");
      else
      {
!       enter_block(cstack);
        ++cstack->cs_trylevel;
        cstack->cs_flags[cstack->cs_idx] = CSF_TRY;
        cstack->cs_pending[cstack->cs_idx] = CSTP_NONE;
***************
*** 1923,1929 ****
         */
        (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE);
  
!       --cstack->cs_idx;
        --cstack->cs_trylevel;
  
        if (!skip)
--- 1967,1973 ----
         */
        (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE);
  
!       leave_block(cstack);
        --cstack->cs_trylevel;
  
        if (!skip)
***************
*** 2303,2309 ****
            --*cond_level;
        if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR)
            free_for_info(cstack->cs_forinfo[cstack->cs_idx]);
!       --cstack->cs_idx;
      }
  }
  
--- 2347,2353 ----
            --*cond_level;
        if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR)
            free_for_info(cstack->cs_forinfo[cstack->cs_idx]);
!       leave_block(cstack);
      }
  }
  
*** ../vim-8.2.1823/src/evalvars.c      2020-10-08 21:30:35.969526619 +0200
--- src/evalvars.c      2020-10-10 18:27:40.229968758 +0200
***************
*** 174,180 ****
  static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, 
char_u *endchars, char_u *op);
  static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, 
void *cookie);
  static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, 
void *cookie);
- static void delete_var(hashtab_T *ht, hashitem_T *hi);
  static void list_one_var(dictitem_T *v, char *prefix, int *first);
  static void list_one_var_a(char *prefix, char_u *name, int type, char_u 
*string, int *first);
  
--- 174,179 ----
***************
*** 2890,2896 ****
   * Delete a variable from hashtab "ht" at item "hi".
   * Clear the variable value and free the dictitem.
   */
!     static void
  delete_var(hashtab_T *ht, hashitem_T *hi)
  {
      dictitem_T        *di = HI2DI(hi);
--- 2889,2895 ----
   * Delete a variable from hashtab "ht" at item "hi".
   * Clear the variable value and free the dictitem.
   */
!     void
  delete_var(hashtab_T *ht, hashitem_T *hi)
  {
      dictitem_T        *di = HI2DI(hi);
*** ../vim-8.2.1823/src/proto/evalvars.pro      2020-10-08 21:16:38.643643838 
+0200
--- src/proto/evalvars.pro      2020-10-10 18:27:29.069998249 +0200
***************
*** 67,72 ****
--- 67,73 ----
  void unref_var_dict(dict_T *dict);
  void vars_clear(hashtab_T *ht);
  void vars_clear_ext(hashtab_T *ht, int free_val);
+ void delete_var(hashtab_T *ht, hashitem_T *hi);
  void set_var(char_u *name, typval_T *tv, int copy);
  void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, 
int flags);
  int var_check_ro(int flags, char_u *name, int use_gettext);
*** ../vim-8.2.1823/src/testdir/test_vim9_script.vim    2020-10-08 
21:16:38.643643838 +0200
--- src/testdir/test_vim9_script.vim    2020-10-10 18:39:33.084055199 +0200
***************
*** 2685,2690 ****
--- 2685,2740 ----
    delete('Xdidcmd')
  enddef
  
+ def Test_script_var_scope()
+   var lines =<< trim END
+       vim9script
+       if true
+         if true
+           var one = 'one'
+           echo one
+         endif
+         echo one
+       endif
+   END
+   CheckScriptFailure(lines, 'E121:', 7)
+ 
+   lines =<< trim END
+       vim9script
+       if true
+         if false
+           var one = 'one'
+           echo one
+         else
+           var one = 'one'
+           echo one
+         endif
+         echo one
+       endif
+   END
+   CheckScriptFailure(lines, 'E121:', 10)
+ 
+   lines =<< trim END
+       vim9script
+       while true
+         var one = 'one'
+         echo one
+         break
+       endwhile
+       echo one
+   END
+   CheckScriptFailure(lines, 'E121:', 7)
+ 
+   lines =<< trim END
+       vim9script
+       for i in range(1)
+         var one = 'one'
+         echo one
+       endfor
+       echo one
+   END
+   CheckScriptFailure(lines, 'E121:', 6)
+ enddef
+ 
  " Keep this last, it messes up highlighting.
  def Test_substitute_cmd()
    new
*** ../vim-8.2.1823/src/version.c       2020-10-10 16:45:20.711469191 +0200
--- src/version.c       2020-10-10 18:42:26.595601471 +0200
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     1824,
  /**/

-- 
Be thankful to be in a traffic jam, because it means you own a car.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202010101707.09AH7fdm3772636%40masaka.moolenaar.net.

Raspunde prin e-mail lui