Patch 8.2.1846
Problem:    Vim9: variables declared in a local block are not found in
            when a function is compiled.
Solution:   Look for script variables in sn_all_vars.
Files:      src/structs.h, src/vim9compile.c, src/proto/vim9compile.pro,
            src/userfunc.c, src/proto/userfunc.pro, src/ex_eval.c,
            src/vim9script.c, src/proto/vim9script.pro, src/vim9execute.c,
            src/testdir/test_vim9_script.vim


*** ../vim-8.2.1845/src/structs.h       2020-10-14 19:39:16.041002546 +0200
--- src/structs.h       2020-10-14 22:23:00.902728717 +0200
***************
*** 1582,1587 ****
--- 1582,1589 ----
      char_u    *uf_va_name;    // name from "...name" or NULL
      type_T    *uf_va_type;    // type from "...name: type" or NULL
      type_T    *uf_func_type;  // type of the function, &t_func_any if unknown
+     int               uf_block_depth; // nr of entries in uf_block_ids
+     int               *uf_block_ids;  // blocks a :def function is defined 
inside
  # if defined(FEAT_LUA)
      cfunc_T     uf_cb;                // callback function for cfunc
      cfunc_free_T uf_cb_free;    // callback function to free cfunc
***************
*** 1792,1798 ****
  
      garray_T  sn_imports;     // imported items, imported_T
      garray_T  sn_type_list;   // keeps types used by variables
!     int               sn_current_block_id;  // Unique ID for each script block
  
      int               sn_version;     // :scriptversion
      int               sn_had_command; // TRUE if any command was executed
--- 1794,1801 ----
  
      garray_T  sn_imports;     // imported items, imported_T
      garray_T  sn_type_list;   // keeps types used by variables
!     int               sn_current_block_id; // ID for current block, 0 for 
outer
!     int               sn_last_block_id;  // Unique ID for each script block
  
      int               sn_version;     // :scriptversion
      int               sn_had_command; // TRUE if any command was executed
*** ../vim-8.2.1845/src/vim9compile.c   2020-10-12 22:07:09.631378864 +0200
--- src/vim9compile.c   2020-10-14 22:56:30.304517513 +0200
***************
*** 195,201 ****
   * Returns OK when found, FAIL otherwise.
   */
      static int
! lookup_arg(
        char_u  *name,
        size_t  len,
        int     *idxp,
--- 195,201 ----
   * Returns OK when found, FAIL otherwise.
   */
      static int
! arg_exists(
        char_u  *name,
        size_t  len,
        int     *idxp,
***************
*** 247,253 ****
      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;
--- 247,253 ----
      if (cctx->ctx_outer != NULL)
      {
        // Lookup the name for an argument of the outer function.
!       if (arg_exists(name, len, idxp, type, gen_load_outer, cctx->ctx_outer)
                                                                         == OK)
        {
            *gen_load_outer = TRUE;
***************
*** 259,264 ****
--- 259,315 ----
  }
  
  /*
+  * Lookup a script-local variable in the current script, possibly defined in a
+  * block that contains the function "cctx->ctx_ufunc".
+  * "cctx" is NULL at the script level.
+  * if "len" is <= 0 "name" must be NUL terminated.
+  * Return NULL when not found.
+  */
+     static sallvar_T *
+ find_script_var(char_u *name, size_t len, cctx_T *cctx)
+ {
+     scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
+     hashitem_T            *hi;
+     int                   cc;
+     sallvar_T     *sav;
+     ufunc_T       *ufunc;
+ 
+     // Find the list of all script variables with the right name.
+     if (len > 0)
+     {
+       cc = name[len];
+       name[len] = NUL;
+     }
+     hi = hash_find(&si->sn_all_vars.dv_hashtab, name);
+     if (len > 0)
+       name[len] = cc;
+     if (HASHITEM_EMPTY(hi))
+       return NULL;
+ 
+     sav = HI2SAV(hi);
+     if (sav->sav_block_id == 0 || cctx == NULL)
+       // variable defined in the script scope or not in a function.
+       return sav;
+ 
+     // Go over the variables with this name and find one that was visible
+     // from the function.
+     ufunc = cctx->ctx_ufunc;
+     while (sav != NULL)
+     {
+       int idx;
+ 
+       // Go over the blocks that this function was defined in.  If the
+       // variable block ID matches it was visible to the function.
+       for (idx = 0; idx < ufunc->uf_block_depth; ++idx)
+           if (ufunc->uf_block_ids[idx] == sav->sav_block_id)
+               return sav;
+       sav = sav->sav_next;
+     }
+ 
+     return NULL;
+ }
+ 
+ /*
   * Returnd TRUE if the script context is Vim9 script.
   */
      static int
***************
*** 268,300 ****
  }
  
  /*
!  * Lookup a variable in the current script.
   * If "vim9script" is TRUE the script must be Vim9 script.  Used for "var"
   * without "s:".
   * Returns OK or FAIL.
   */
      static int
! lookup_script(char_u *name, size_t len, int vim9script)
  {
!     int                   cc;
!     hashtab_T     *ht;
!     dictitem_T            *di;
  
      if (current_sctx.sc_sid <= 0)
        return FAIL;
!     ht = &SCRIPT_VARS(current_sctx.sc_sid);
!     if (vim9script && !script_is_vim9())
        return FAIL;
!     cc = name[len];
!     name[len] = NUL;
!     di = find_var_in_ht(ht, 0, name, TRUE);
!     name[len] = cc;
!     return di == NULL ? FAIL: OK;
  }
  
  /*
   * Check if "p[len]" is already defined, either in script "import_sid" or in
!  * compilation context "cctx".
   * Does not check the global namespace.
   * Return FAIL and give an error if it defined.
   */
--- 319,368 ----
  }
  
  /*
!  * Lookup a variable (without s: prefix) in the current script.
   * If "vim9script" is TRUE the script must be Vim9 script.  Used for "var"
   * without "s:".
+  * "cctx" is NULL at the script level.
   * Returns OK or FAIL.
   */
      static int
! script_var_exists(char_u *name, size_t len, int vim9script, cctx_T *cctx)
  {
!     int                   is_vim9_script;
  
      if (current_sctx.sc_sid <= 0)
        return FAIL;
!     is_vim9_script = script_is_vim9();
!     if (vim9script && !is_vim9_script)
        return FAIL;
!     if (is_vim9_script)
!     {
!       // Check script variables that were visible where the function was
!       // defined.
!       if (find_script_var(name, len, cctx) != NULL)
!           return OK;
!     }
!     else
!     {
!       hashtab_T       *ht = &SCRIPT_VARS(current_sctx.sc_sid);
!       dictitem_T      *di;
!       int             cc;
! 
!       // Check script variables that are currently visible
!       cc = name[len];
!       name[len] = NUL;
!       di = find_var_in_ht(ht, 0, name, TRUE);
!       name[len] = cc;
!       if (di != NULL)
!           return OK;
!     }
! 
!     return FAIL;
  }
  
  /*
   * Check if "p[len]" is already defined, either in script "import_sid" or in
!  * compilation context "cctx".  "cctx" is NULL at the script level.
   * Does not check the global namespace.
   * Return FAIL and give an error if it defined.
   */
***************
*** 305,314 ****
      ufunc_T   *ufunc = NULL;
  
      p[len] = NUL;
!     if (lookup_script(p, len, FALSE) == OK
            || (cctx != NULL
                && (lookup_local(p, len, cctx) != NULL
!                   || lookup_arg(p, len, NULL, NULL, NULL, cctx) == OK))
            || find_imported(p, len, cctx) != NULL
            || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL)
      {
--- 373,382 ----
      ufunc_T   *ufunc = NULL;
  
      p[len] = NUL;
!     if (script_var_exists(p, len, FALSE, cctx) == OK
            || (cctx != NULL
                && (lookup_local(p, len, cctx) != NULL
!                   || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK))
            || find_imported(p, len, cctx) != NULL
            || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL)
      {
***************
*** 1699,1705 ****
  {
      lvar_T  *lvar;
  
!     if (lookup_arg(name, len, NULL, NULL, NULL, cctx) == OK)
      {
        emsg_namelen(_(e_str_is_used_as_argument), name, (int)len);
        return NULL;
--- 1767,1773 ----
  {
      lvar_T  *lvar;
  
!     if (arg_exists(name, len, NULL, NULL, NULL, cctx) == OK)
      {
        emsg_namelen(_(e_str_is_used_as_argument), name, (int)len);
        return NULL;
***************
*** 1760,1775 ****
   * If not found returns -2.
   */
      int
! get_script_item_idx(int sid, char_u *name, int check_writable)
  {
      hashtab_T     *ht;
      dictitem_T            *di;
      scriptitem_T    *si = SCRIPT_ITEM(sid);
      int                   idx;
  
-     // First look the name up in the hashtable.
      if (!SCRIPT_ID_VALID(sid))
        return -1;
      ht = &SCRIPT_VARS(sid);
      di = find_var_in_ht(ht, 0, name, TRUE);
      if (di == NULL)
--- 1828,1857 ----
   * If not found returns -2.
   */
      int
! get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx)
  {
      hashtab_T     *ht;
      dictitem_T            *di;
      scriptitem_T    *si = SCRIPT_ITEM(sid);
+     svar_T        *sv;
      int                   idx;
  
      if (!SCRIPT_ID_VALID(sid))
        return -1;
+     if (sid == current_sctx.sc_sid)
+     {
+       sallvar_T *sav = find_script_var(name, (size_t)-1, cctx);
+ 
+       if (sav == NULL)
+           return -2;
+       idx = sav->sav_var_vals_idx;
+       sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
+       if (check_writable && sv->sv_const)
+           semsg(_(e_readonlyvar), name);
+       return idx;
+     }
+ 
+     // First look the name up in the hashtable.
      ht = &SCRIPT_VARS(sid);
      di = find_var_in_ht(ht, 0, name, TRUE);
      if (di == NULL)
***************
*** 1778,1785 ****
      // Now find the svar_T index in sn_var_vals.
      for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx)
      {
!       svar_T    *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
! 
        if (sv->sv_tv == &di->di_tv)
        {
            if (check_writable && sv->sv_const)
--- 1860,1866 ----
      // Now find the svar_T index in sn_var_vals.
      for (idx = 0; idx < si->sn_var_vals.ga_len; ++idx)
      {
!       sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
        if (sv->sv_tv == &di->di_tv)
        {
            if (check_writable && sv->sv_const)
***************
*** 2083,2089 ****
      if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
        return FAIL;
      si = SCRIPT_ITEM(current_sctx.sc_sid);
!     idx = get_script_item_idx(current_sctx.sc_sid, name, FALSE);
      if (idx == -1 || si->sn_version != SCRIPT_VERSION_VIM9)
      {
        // variable is not in sn_var_vals: old style script.
--- 2164,2170 ----
      if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
        return FAIL;
      si = SCRIPT_ITEM(current_sctx.sc_sid);
!     idx = get_script_item_idx(current_sctx.sc_sid, name, FALSE, cctx);
      if (idx == -1 || si->sn_version != SCRIPT_VERSION_VIM9)
      {
        // variable is not in sn_var_vals: old style script.
***************
*** 2130,2136 ****
            cc = *p;
            *p = NUL;
  
!           idx = find_exported(import->imp_sid, exp_name, &ufunc, &type);
            *p = cc;
            p = skipwhite(p);
  
--- 2211,2217 ----
            cc = *p;
            *p = NUL;
  
!           idx = find_exported(import->imp_sid, exp_name, &ufunc, &type, cctx);
            *p = cc;
            p = skipwhite(p);
  
***************
*** 2257,2263 ****
        if (name == NULL)
            return FAIL;
  
!       if (lookup_arg(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK)
        {
            if (!gen_load_outer)
                gen_load = TRUE;
--- 2338,2344 ----
        if (name == NULL)
            return FAIL;
  
!       if (arg_exists(*arg, len, &idx, &type, &gen_load_outer, cctx) == OK)
        {
            if (!gen_load_outer)
                gen_load = TRUE;
***************
*** 2279,2285 ****
            {
                // "var" can be script-local even without using "s:" if it
                // already exists in a Vim9 script or when it's imported.
!               if (lookup_script(*arg, len, TRUE) == OK
                        || find_imported(name, 0, cctx) != NULL)
                   res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE);
  
--- 2360,2366 ----
            {
                // "var" can be script-local even without using "s:" if it
                // already exists in a Vim9 script or when it's imported.
!               if (script_var_exists(*arg, len, TRUE, cctx) == OK
                        || find_imported(name, 0, cctx) != NULL)
                   res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE);
  
***************
*** 4468,4474 ****
      eap->skip = cctx->ctx_skip == SKIP_YES;
      eap->forceit = FALSE;
      lambda_name = get_lambda_name();
!     ufunc = def_function(eap, lambda_name);
  
      if (ufunc == NULL)
        return eap->skip ? (char_u *)"" : NULL;
--- 4549,4555 ----
      eap->skip = cctx->ctx_skip == SKIP_YES;
      eap->forceit = FALSE;
      lambda_name = get_lambda_name();
!     ufunc = define_function(eap, lambda_name);
  
      if (ufunc == NULL)
        return eap->skip ? (char_u *)"" : NULL;
***************
*** 4494,4505 ****
--- 4575,4599 ----
        // Define a local variable for the function reference.
        lvar_T  *lvar = reserve_local(cctx, name_start, name_end - name_start,
                                                    TRUE, ufunc->uf_func_type);
+       int block_depth = cctx->ctx_ufunc->uf_block_depth;
  
        if (lvar == NULL)
            return NULL;
        if (generate_FUNCREF(cctx, ufunc) == FAIL)
            return NULL;
        r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
+ 
+       // copy over the block scope IDs
+       if (block_depth > 0)
+       {
+           ufunc->uf_block_ids = ALLOC_MULT(int, block_depth);
+           if (ufunc->uf_block_ids != NULL)
+           {
+               mch_memmove(ufunc->uf_block_ids, cctx->ctx_ufunc->uf_block_ids,
+                                                   sizeof(int) * block_depth);
+               ufunc->uf_block_depth = block_depth;
+           }
+       }
      }
  
      // TODO: warning for trailing text?
***************
*** 4900,4906 ****
                if (lvar == NULL)
                {
                    CLEAR_FIELD(arg_lvar);
!                   if (lookup_arg(var_start, varlen,
                                &arg_lvar.lv_idx, &arg_lvar.lv_type,
                                &arg_lvar.lv_from_outer, cctx) == OK)
                    {
--- 4994,5000 ----
                if (lvar == NULL)
                {
                    CLEAR_FIELD(arg_lvar);
!                   if (arg_exists(var_start, varlen,
                                &arg_lvar.lv_idx, &arg_lvar.lv_type,
                                &arg_lvar.lv_from_outer, cctx) == OK)
                    {
***************
*** 4925,4932 ****
                    int script_namespace = varlen > 1
                                           && STRNCMP(var_start, "s:", 2) == 0;
                    int script_var = (script_namespace
!                             ? lookup_script(var_start + 2, varlen - 2, FALSE)
!                             : lookup_script(var_start, varlen, TRUE)) == OK;
                    imported_T  *import =
                                        find_imported(var_start, varlen, cctx);
  
--- 5019,5028 ----
                    int script_namespace = varlen > 1
                                           && STRNCMP(var_start, "s:", 2) == 0;
                    int script_var = (script_namespace
!                             ? script_var_exists(var_start + 2, varlen - 2,
!                                                                  FALSE, cctx)
!                             : script_var_exists(var_start, varlen,
!                                                           TRUE, cctx)) == OK;
                    imported_T  *import =
                                        find_imported(var_start, varlen, cctx);
  
***************
*** 4962,4968 ****
                        if (SCRIPT_ID_VALID(scriptvar_sid))
                        {
                            scriptvar_idx = get_script_item_idx(scriptvar_sid,
!                                                               rawname, TRUE);
                            if (scriptvar_idx >= 0)
                            {
                                scriptitem_T *si = SCRIPT_ITEM(scriptvar_sid);
--- 5058,5064 ----
                        if (SCRIPT_ID_VALID(scriptvar_sid))
                        {
                            scriptvar_idx = get_script_item_idx(scriptvar_sid,
!                                                         rawname, TRUE, cctx);
                            if (scriptvar_idx >= 0)
                            {
                                scriptitem_T *si = SCRIPT_ITEM(scriptvar_sid);
***************
*** 6964,6972 ****
                            || *ea.cmd == '@'
                            || ((len) > 2 && ea.cmd[1] == ':')
                            || lookup_local(ea.cmd, len, &cctx) != NULL
!                           || lookup_arg(ea.cmd, len, NULL, NULL,
                                                             NULL, &cctx) == OK
!                           || lookup_script(ea.cmd, len, FALSE) == OK
                            || find_imported(ea.cmd, len, &cctx) != NULL)
                    {
                        line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
--- 7060,7069 ----
                            || *ea.cmd == '@'
                            || ((len) > 2 && ea.cmd[1] == ':')
                            || lookup_local(ea.cmd, len, &cctx) != NULL
!                           || arg_exists(ea.cmd, len, NULL, NULL,
                                                             NULL, &cctx) == OK
!                           || script_var_exists(ea.cmd, len,
!                                                           FALSE, &cctx) == OK
                            || find_imported(ea.cmd, len, &cctx) != NULL)
                    {
                        line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
*** ../vim-8.2.1845/src/proto/vim9compile.pro   2020-09-19 15:16:46.395622457 
+0200
--- src/proto/vim9compile.pro   2020-10-14 21:15:29.052855837 +0200
***************
*** 1,7 ****
  /* vim9compile.c */
  int check_defined(char_u *p, size_t len, cctx_T *cctx);
  int check_compare_types(exptype_T type, typval_T *tv1, typval_T *tv2);
! int get_script_item_idx(int sid, char_u *name, int check_writable);
  imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
  imported_T *find_imported_in_script(char_u *name, size_t len, int sid);
  int vim9_comment_start(char_u *p);
--- 1,7 ----
  /* vim9compile.c */
  int check_defined(char_u *p, size_t len, cctx_T *cctx);
  int check_compare_types(exptype_T type, typval_T *tv1, typval_T *tv2);
! int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T 
*cctx);
  imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
  imported_T *find_imported_in_script(char_u *name, size_t len, int sid);
  int vim9_comment_start(char_u *p);
*** ../vim-8.2.1845/src/userfunc.c      2020-10-04 13:42:30.299568231 +0200
--- src/userfunc.c      2020-10-14 22:38:53.471793794 +0200
***************
*** 1134,1139 ****
--- 1134,1140 ----
      ga_clear_strings(&(fp->uf_lines));
      VIM_CLEAR(fp->uf_arg_types);
      VIM_CLEAR(fp->uf_def_arg_idx);
+     VIM_CLEAR(fp->uf_block_ids);
      VIM_CLEAR(fp->uf_va_name);
      clear_type_list(&fp->uf_type_list);
  
***************
*** 2658,2664 ****
   * Returns a pointer to the function or NULL if no function defined.
   */
      ufunc_T *
! def_function(exarg_T *eap, char_u *name_arg)
  {
      char_u    *theline;
      char_u    *line_to_free = NULL;
--- 2659,2665 ----
   * Returns a pointer to the function or NULL if no function defined.
   */
      ufunc_T *
! define_function(exarg_T *eap, char_u *name_arg)
  {
      char_u    *theline;
      char_u    *line_to_free = NULL;
***************
*** 3477,3485 ****
        // error messages are for the first function line
        SOURCING_LNUM = sourcing_lnum_top;
  
        // parse the argument types
        ga_init2(&fp->uf_type_list, sizeof(type_T *), 10);
- 
        if (argtypes.ga_len > 0)
        {
            // When "varargs" is set the last name/type goes into uf_va_name
--- 3478,3501 ----
        // error messages are for the first function line
        SOURCING_LNUM = sourcing_lnum_top;
  
+       if (eap->cstack != NULL && eap->cstack->cs_idx >= 0)
+       {
+           int count = eap->cstack->cs_idx + 1;
+ 
+           // The block context may be needed for script variables declared in
+           // a block visible now but not when the function is compiled.
+           fp->uf_block_ids = ALLOC_MULT(int, count);
+           if (fp->uf_block_ids != NULL)
+           {
+               mch_memmove(fp->uf_block_ids, eap->cstack->cs_block_id,
+                                                         sizeof(int) * count);
+               fp->uf_block_depth = count;
+           }
+           // TODO: set flag in each block to indicate a function was defined
+       }
+ 
        // parse the argument types
        ga_init2(&fp->uf_type_list, sizeof(type_T *), 10);
        if (argtypes.ga_len > 0)
        {
            // When "varargs" is set the last name/type goes into uf_va_name
***************
*** 3608,3614 ****
      void
  ex_function(exarg_T *eap)
  {
!     (void)def_function(eap, NULL);
  }
  
  /*
--- 3624,3630 ----
      void
  ex_function(exarg_T *eap)
  {
!     (void)define_function(eap, NULL);
  }
  
  /*
*** ../vim-8.2.1845/src/proto/userfunc.pro      2020-09-19 18:19:15.667278827 
+0200
--- src/proto/userfunc.pro      2020-10-14 21:03:05.719433478 +0200
***************
*** 29,35 ****
  char_u *printable_func_name(ufunc_T *fp);
  char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, 
funcdict_T *fdp, partial_T **partial);
  char_u *untrans_function_name(char_u *name);
! ufunc_T *def_function(exarg_T *eap, char_u *name_arg);
  void ex_function(exarg_T *eap);
  void ex_defcompile(exarg_T *eap);
  int eval_fname_script(char_u *p);
--- 29,35 ----
  char_u *printable_func_name(ufunc_T *fp);
  char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, 
funcdict_T *fdp, partial_T **partial);
  char_u *untrans_function_name(char_u *name);
! ufunc_T *define_function(exarg_T *eap, char_u *name_arg);
  void ex_function(exarg_T *eap);
  void ex_defcompile(exarg_T *eap);
  int eval_fname_script(char_u *p);
*** ../vim-8.2.1845/src/ex_eval.c       2020-10-14 19:39:16.041002546 +0200
--- src/ex_eval.c       2020-10-15 12:13:34.818381060 +0200
***************
*** 918,924 ****
        scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
  
        cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len;
!       cstack->cs_block_id[cstack->cs_idx] = ++si->sn_current_block_id;
      }
  }
  
--- 918,925 ----
        scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
  
        cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len;
!       cstack->cs_block_id[cstack->cs_idx] = ++si->sn_last_block_id;
!       si->sn_current_block_id = si->sn_last_block_id;
      }
  }
  
***************
*** 938,948 ****
            if (sv->sv_name != NULL)
                // Remove a variable declared inside the block, if it still
                // exists, from sn_vars and move the value into sn_all_vars.
!               hide_script_var(si, sv);
        }
  
        // TODO: is this needed?
        cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len;
      }
      --cstack->cs_idx;
  }
--- 939,954 ----
            if (sv->sv_name != NULL)
                // Remove a variable declared inside the block, if it still
                // exists, from sn_vars and move the value into sn_all_vars.
!               hide_script_var(si, i);
        }
  
        // TODO: is this needed?
        cstack->cs_script_var_len[cstack->cs_idx] = si->sn_var_vals.ga_len;
+ 
+       if (cstack->cs_idx == 0)
+           si->sn_current_block_id = 0;
+       else
+           si->sn_current_block_id = cstack->cs_block_id[cstack->cs_idx - 1];
      }
      --cstack->cs_idx;
  }
*** ../vim-8.2.1845/src/vim9script.c    2020-10-14 19:39:16.041002546 +0200
--- src/vim9script.c    2020-10-15 12:13:07.746459477 +0200
***************
*** 193,199 ****
        int         sid,
        char_u      *name,
        ufunc_T     **ufunc,
!       type_T      **type)
  {
      int               idx = -1;
      svar_T    *sv;
--- 193,200 ----
        int         sid,
        char_u      *name,
        ufunc_T     **ufunc,
!       type_T      **type,
!       cctx_T      *cctx)
  {
      int               idx = -1;
      svar_T    *sv;
***************
*** 201,207 ****
  
      // find name in "script"
      // TODO: also find script-local user function
!     idx = get_script_item_idx(sid, name, FALSE);
      if (idx >= 0)
      {
        sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
--- 202,208 ----
  
      // find name in "script"
      // TODO: also find script-local user function
!     idx = get_script_item_idx(sid, name, FALSE, cctx);
      if (idx >= 0)
      {
        sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
***************
*** 248,253 ****
--- 249,255 ----
  /*
   * Handle an ":import" command and add the resulting imported_T to "gap", when
   * not NULL, or script "import_sid" sn_imports.
+  * "cctx" is NULL at the script level.
   * Returns a pointer to after the command or NULL in case of failure
   */
      char_u *
***************
*** 461,467 ****
            ufunc_T     *ufunc = NULL;
            type_T      *type;
  
!           idx = find_exported(sid, name, &ufunc, &type);
  
            if (idx < 0 && ufunc == NULL)
                goto erret;
--- 463,469 ----
            ufunc_T     *ufunc = NULL;
            type_T      *type;
  
!           idx = find_exported(sid, name, &ufunc, &type, cctx);
  
            if (idx < 0 && ufunc == NULL)
                goto erret;
***************
*** 623,631 ****
      }
  }
  
      void
! hide_script_var(scriptitem_T *si, svar_T *sv)
  {
      hashtab_T *script_ht = get_script_local_ht();
      hashtab_T *all_ht = &si->sn_all_vars.dv_hashtab;
      hashitem_T        *script_hi;
--- 625,638 ----
      }
  }
  
+ /*
+  * Hide a script variable when leaving a block.
+  * "idx" is de index in sn_var_vals.
+  */
      void
! hide_script_var(scriptitem_T *si, int idx)
  {
+     svar_T    *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
      hashtab_T *script_ht = get_script_local_ht();
      hashtab_T *all_ht = &si->sn_all_vars.dv_hashtab;
      hashitem_T        *script_hi;
***************
*** 640,650 ****
        dictitem_T      *di = HI2DI(script_hi);
        sallvar_T       *sav = HI2SAV(all_hi);
  
!       sav->sav_tv = di->di_tv;
!       di->di_tv.v_type = VAR_UNKNOWN;
!       sav->sav_flags = di->di_flags;
!       sav->sav_di = NULL;
!       delete_var(script_ht, script_hi);
      }
  }
  
--- 647,665 ----
        dictitem_T      *di = HI2DI(script_hi);
        sallvar_T       *sav = HI2SAV(all_hi);
  
!       // There can be multiple entries with the same name in different
!       // blocks, find the right one.
!       while (sav != NULL && sav->sav_var_vals_idx != idx)
!           sav = sav->sav_next;
!       if (sav != NULL)
!       {
!           sav->sav_tv = di->di_tv;
!           di->di_tv.v_type = VAR_UNKNOWN;
!           sav->sav_flags = di->di_flags;
!           sav->sav_di = NULL;
!           delete_var(script_ht, script_hi);
!           sv->sv_tv = &sav->sav_tv;
!       }
      }
  }
  
*** ../vim-8.2.1845/src/proto/vim9script.pro    2020-10-14 19:39:16.041002546 
+0200
--- src/proto/vim9script.pro    2020-10-15 12:13:41.850360716 +0200
***************
*** 5,15 ****
  void ex_export(exarg_T *eap);
  void free_imports_and_script_vars(int sid);
  void ex_import(exarg_T *eap);
! int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type);
  char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, 
evalarg_T *evalarg, void *cctx);
  char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
  void add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type);
! void hide_script_var(scriptitem_T *si, svar_T *sv);
  void free_all_script_vars(scriptitem_T *si);
  svar_T *find_typval_in_script(typval_T *dest);
  int check_script_var_type(typval_T *dest, typval_T *value, char_u *name);
--- 5,15 ----
  void ex_export(exarg_T *eap);
  void free_imports_and_script_vars(int sid);
  void ex_import(exarg_T *eap);
! int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, 
cctx_T *cctx);
  char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, 
evalarg_T *evalarg, void *cctx);
  char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
  void add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type);
! void hide_script_var(scriptitem_T *si, int idx);
  void free_all_script_vars(scriptitem_T *si);
  svar_T *find_typval_in_script(typval_T *dest);
  int check_script_var_type(typval_T *dest, typval_T *value, char_u *name);
*** ../vim-8.2.1845/src/vim9execute.c   2020-10-10 14:12:58.024646147 +0200
--- src/vim9execute.c   2020-10-15 12:16:32.313868932 +0200
***************
*** 2962,2969 ****
                    svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
                                             + iptr->isn_arg.script.script_idx;
  
!                   smsg("%4d LOADSCRIPT %s from %s", current,
!                                                    sv->sv_name, si->sn_name);
                }
                break;
            case ISN_LOADS:
--- 2962,2971 ----
                    svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
                                             + iptr->isn_arg.script.script_idx;
  
!                   smsg("%4d LOADSCRIPT %s-%d from %s", current,
!                                           sv->sv_name,
!                                           iptr->isn_arg.script.script_idx,
!                                           si->sn_name);
                }
                break;
            case ISN_LOADS:
***************
*** 3054,3061 ****
                    svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
                                             + iptr->isn_arg.script.script_idx;
  
!                   smsg("%4d STORESCRIPT %s in %s", current,
!                                                    sv->sv_name, si->sn_name);
                }
                break;
            case ISN_STOREOPT:
--- 3056,3065 ----
                    svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
                                             + iptr->isn_arg.script.script_idx;
  
!                   smsg("%4d STORESCRIPT %s-%d in %s", current,
!                                            sv->sv_name,
!                                            iptr->isn_arg.script.script_idx,
!                                            si->sn_name);
                }
                break;
            case ISN_STOREOPT:
*** ../vim-8.2.1845/src/testdir/test_vim9_script.vim    2020-10-10 
21:33:42.407033514 +0200
--- src/testdir/test_vim9_script.vim    2020-10-15 12:21:46.588968472 +0200
***************
*** 250,255 ****
--- 250,285 ----
    CheckDefFailure(['{', 'echo 1'], 'E1026:')
  enddef
  
+ def Test_block_local_vars()
+   var lines =<< trim END
+       vim9script
+       if true
+         var text = 'hello'
+         def SayHello(): string
+           return text
+         enddef
+         def SetText(v: string)
+           text = v
+         enddef
+       endif
+ 
+       if true
+         var text = 'again'
+         def SayAgain(): string
+           return text
+         enddef
+       endif
+       defcompile
+ 
+       assert_equal('hello', SayHello())
+       assert_equal('again', SayAgain())
+ 
+       SetText('foobar')
+       assert_equal('foobar', SayHello())
+   END
+   CheckScriptSuccess(lines)
+ enddef
+ 
  func g:NoSuchFunc()
    echo 'none'
  endfunc
***************
*** 1265,1279 ****
  
    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.*' ..
!           '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)
  
    Undo_export_script_lines()
--- 1295,1310 ----
  
    assert_equal(9876, g:imported_abs)
    assert_equal(8888, g:imported_after)
!   assert_match('<SNR>\d\+_UseExported\_s*' ..
!           'g:imported_abs = exported\_s*' ..
!           '0 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' ..
!           '1 STOREG g:imported_abs\_s*' ..
!           'exported = 8888\_s*' ..
!           '2 PUSHNR 8888\_s*' ..
!           '3 STORESCRIPT exported-2 in .*Xexport_abs.vim\_s*' ..
!           'g:imported_after = exported\_s*' ..
!           '4 LOADSCRIPT exported-2 from .*Xexport_abs.vim\_s*' ..
!           '5 STOREG g:imported_after',
          g:import_disassembled)
  
    Undo_export_script_lines()
*** ../vim-8.2.1845/src/version.c       2020-10-14 19:39:16.041002546 +0200
--- src/version.c       2020-10-15 12:44:31.001534972 +0200
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     1846,
  /**/

-- 
>From "know your smileys":
 :~)    A man with a tape recorder up his nose

 /// 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/202010151047.09FAlF8a041607%40masaka.moolenaar.net.

Raspunde prin e-mail lui