Patch 9.0.0949
Problem:    Crash when unletting a variable while listing variables.
Solution:   Disallow changing a hashtable while going over the entries.
            (closes #11435)
Files:      src/structs.h, src/buffer.c, src/dict.c, src/proto/dict.pro,
            src/errors.h, src/evalvars.c, src/hashtab.c,
            src/proto/hashtab.pro, src/if_lua.c, src/if_ruby.c,
            src/if_py_both.h, src/spellfile.c, src/sign.c, src/syntax.c,
            src/terminal.c, src/textprop.c, src/vim9execute.c,
            src/vim9script.c, src/userfunc.c, src/testdir/test_autocmd.vim


*** ../vim-9.0.0948/src/structs.h       2022-11-24 13:27:32.389881082 +0000
--- src/structs.h       2022-11-25 15:33:50.290581762 +0000
***************
*** 1313,1318 ****
--- 1313,1324 ----
  // This allows for storing 10 items (2/3 of 16) before a resize is needed.
  #define HT_INIT_SIZE 16
  
+ // flags used for ht_flags
+ #define HTFLAGS_ERROR 0x01    // Set when growing failed, can't add more
+                               // items before growing works.
+ #define HTFLAGS_FROZEN        0x02    // Trying to add or remove an item will 
result
+                               // in an error message.
+ 
  typedef struct hashtable_S
  {
      long_u    ht_mask;        // mask used for hash value (nr of items in
***************
*** 1321,1328 ****
      long_u    ht_filled;      // number of items used + removed
      int               ht_changed;     // incremented when adding or removing 
an item
      int               ht_locked;      // counter for hash_lock()
!     int               ht_error;       // when set growing failed, can't add 
more
!                               // items before growing works
      hashitem_T        *ht_array;      // points to the array, allocated when 
it's
                                // not "ht_smallarray"
      hashitem_T        ht_smallarray[HT_INIT_SIZE];   // initial array
--- 1327,1333 ----
      long_u    ht_filled;      // number of items used + removed
      int               ht_changed;     // incremented when adding or removing 
an item
      int               ht_locked;      // counter for hash_lock()
!     int               ht_flags;       // HTFLAGS_ values
      hashitem_T        *ht_array;      // points to the array, allocated when 
it's
                                // not "ht_smallarray"
      hashitem_T        ht_smallarray[HT_INIT_SIZE];   // initial array
*** ../vim-9.0.0948/src/buffer.c        2022-11-07 12:16:46.393761740 +0000
--- src/buffer.c        2022-11-25 15:51:53.973205560 +0000
***************
*** 434,440 ****
  buf_hashtab_add(buf_T *buf)
  {
      sprintf((char *)buf->b_key, "%x", buf->b_fnum);
!     if (hash_add(&buf_hashtab, buf->b_key) == FAIL)
        emsg(_(e_buffer_cannot_be_registered));
  }
  
--- 434,440 ----
  buf_hashtab_add(buf_T *buf)
  {
      sprintf((char *)buf->b_key, "%x", buf->b_fnum);
!     if (hash_add(&buf_hashtab, buf->b_key, "create buffer") == FAIL)
        emsg(_(e_buffer_cannot_be_registered));
  }
  
***************
*** 444,450 ****
      hashitem_T *hi = hash_find(&buf_hashtab, buf->b_key);
  
      if (!HASHITEM_EMPTY(hi))
!       hash_remove(&buf_hashtab, hi);
  }
  
  /*
--- 444,450 ----
      hashitem_T *hi = hash_find(&buf_hashtab, buf->b_key);
  
      if (!HASHITEM_EMPTY(hi))
!       hash_remove(&buf_hashtab, hi, "close buffer");
  }
  
  /*
***************
*** 925,931 ****
      free_buffer_stuff(buf, TRUE);
  #ifdef FEAT_EVAL
      // b:changedtick uses an item in buf_T, remove it now
!     dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di);
      unref_var_dict(buf->b_vars);
      remove_listeners(buf);
  #endif
--- 925,931 ----
      free_buffer_stuff(buf, TRUE);
  #ifdef FEAT_EVAL
      // b:changedtick uses an item in buf_T, remove it now
!     dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di, "free buffer");
      unref_var_dict(buf->b_vars);
      remove_listeners(buf);
  #endif
*** ../vim-9.0.0948/src/dict.c  2022-11-23 11:33:57.294027200 +0000
--- src/dict.c  2022-11-25 16:15:43.015168141 +0000
***************
*** 122,127 ****
--- 122,130 ----
      hashitem_T        *hi;
      dictitem_T        *di;
  
+     if (check_hashtab_frozen(ht, "clear dict"))
+       return;
+ 
      // Lock the hashtab, we don't want it to resize while freeing items.
      hash_lock(ht);
      todo = (int)ht->ht_used;
***************
*** 132,138 ****
            // Remove the item before deleting it, just in case there is
            // something recursive causing trouble.
            di = HI2DI(hi);
!           hash_remove(ht, hi);
            dictitem_free(di);
            --todo;
        }
--- 135,141 ----
            // Remove the item before deleting it, just in case there is
            // something recursive causing trouble.
            di = HI2DI(hi);
!           hash_remove(ht, hi, "clear dict");
            dictitem_free(di);
            --todo;
        }
***************
*** 256,264 ****
  
  /*
   * Remove item "item" from Dictionary "dict" and free it.
   */
      void
! dictitem_remove(dict_T *dict, dictitem_T *item)
  {
      hashitem_T        *hi;
  
--- 259,268 ----
  
  /*
   * Remove item "item" from Dictionary "dict" and free it.
+  * "command" is used for the error message when the hashtab if frozen.
   */
      void
! dictitem_remove(dict_T *dict, dictitem_T *item, char *command)
  {
      hashitem_T        *hi;
  
***************
*** 266,272 ****
      if (HASHITEM_EMPTY(hi))
        internal_error("dictitem_remove()");
      else
!       hash_remove(&dict->dv_hashtab, hi);
      dictitem_free(item);
  }
  
--- 270,276 ----
      if (HASHITEM_EMPTY(hi))
        internal_error("dictitem_remove()");
      else
!       hash_remove(&dict->dv_hashtab, hi, command);
      dictitem_free(item);
  }
  
***************
*** 375,381 ****
  {
      if (dict_wrong_func_name(d, &item->di_tv, item->di_key))
        return FAIL;
!     return hash_add(&d->dv_hashtab, item->di_key);
  }
  
  /*
--- 379,385 ----
  {
      if (dict_wrong_func_name(d, &item->di_tv, item->di_key))
        return FAIL;
!     return hash_add(&d->dv_hashtab, item->di_key, "add to dictionary");
  }
  
  /*
***************
*** 1094,1107 ****
      char_u    *arg_errmsg = (char_u *)N_("extend() argument");
      type_T    *type;
  
      if (d1->dv_type != NULL && d1->dv_type->tt_member != NULL)
        type = d1->dv_type->tt_member;
      else
        type = NULL;
  
-     if (*action == 'm')
-       hash_lock(&d2->dv_hashtab);  // don't rehash on hash_remove()
- 
      todo = (int)d2->dv_hashtab.ht_used;
      for (hashitem_T *hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
      {
--- 1098,1118 ----
      char_u    *arg_errmsg = (char_u *)N_("extend() argument");
      type_T    *type;
  
+     if (check_hashtab_frozen(&d1->dv_hashtab, "extend"))
+       return;
+ 
+     if (*action == 'm')
+     {
+       if (check_hashtab_frozen(&d2->dv_hashtab, "extend"))
+           return;
+       hash_lock(&d2->dv_hashtab);  // don't rehash on hash_remove()
+     }
+ 
      if (d1->dv_type != NULL && d1->dv_type->tt_member != NULL)
        type = d1->dv_type->tt_member;
      else
        type = NULL;
  
      todo = (int)d2->dv_hashtab.ht_used;
      for (hashitem_T *hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
      {
***************
*** 1126,1132 ****
                    // If dict_add() fails then "d2" won't be empty.
                    di1 = HI2DI(hi2);
                    if (dict_add(d1, di1) == OK)
!                       hash_remove(&d2->dv_hashtab, hi2);
                }
                else
                {
--- 1137,1143 ----
                    // If dict_add() fails then "d2" won't be empty.
                    di1 = HI2DI(hi2);
                    if (dict_add(d1, di1) == OK)
!                       hash_remove(&d2->dv_hashtab, hi2, "extend");
                }
                else
                {
***************
*** 1406,1412 ****
                if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
                        || var_check_ro(di->di_flags, arg_errmsg, TRUE))
                    break;
!               dictitem_remove(d, di);
            }
        }
      }
--- 1417,1423 ----
                if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
                        || var_check_ro(di->di_flags, arg_errmsg, TRUE))
                    break;
!               dictitem_remove(d, di, "filter");
            }
        }
      }
***************
*** 1453,1459 ****
  
      *rettv = di->di_tv;
      init_tv(&di->di_tv);
!     dictitem_remove(d, di);
  }
  
  typedef enum {
--- 1464,1470 ----
  
      *rettv = di->di_tv;
      init_tv(&di->di_tv);
!     dictitem_remove(d, di, "remove()");
  }
  
  typedef enum {
*** ../vim-9.0.0948/src/proto/dict.pro  2022-07-23 09:52:00.333814262 +0100
--- src/proto/dict.pro  2022-11-25 16:14:45.703129007 +0000
***************
*** 10,16 ****
  int dict_free_nonref(int copyID);
  void dict_free_items(int copyID);
  dictitem_T *dictitem_alloc(char_u *key);
! void dictitem_remove(dict_T *dict, dictitem_T *item);
  void dictitem_free(dictitem_T *item);
  dict_T *dict_copy(dict_T *orig, int deep, int top, int copyID);
  int dict_wrong_func_name(dict_T *d, typval_T *tv, char_u *name);
--- 10,16 ----
  int dict_free_nonref(int copyID);
  void dict_free_items(int copyID);
  dictitem_T *dictitem_alloc(char_u *key);
! void dictitem_remove(dict_T *dict, dictitem_T *item, char *command);
  void dictitem_free(dictitem_T *item);
  dict_T *dict_copy(dict_T *orig, int deep, int top, int copyID);
  int dict_wrong_func_name(dict_T *d, typval_T *tv, char_u *name);
*** ../vim-9.0.0948/src/errors.h        2022-11-19 11:41:26.341764390 +0000
--- src/errors.h        2022-11-25 15:43:23.962714750 +0000
***************
*** 3343,3345 ****
--- 3343,3347 ----
        INIT(= N_("E1311: Cannot change user commands while listing"));
  EXTERN char e_not_allowed_to_change_window_layout_in_this_autocmd[]
        INIT(= N_("E1312: Not allowed to change the window layout in this 
autocmd"));
+ EXTERN char e_not_allowed_to_add_or_remove_entries_str[]
+       INIT(= N_("E1313: Not allowed to add or remove entries (%s)"));
*** ../vim-9.0.0948/src/evalvars.c      2022-11-16 20:33:17.088528372 +0000
--- src/evalvars.c      2022-11-25 16:22:31.953763428 +0000
***************
*** 217,226 ****
  
        // add to v: scope dict, unless the value is not always available
        if (p->vv_tv_type != VAR_UNKNOWN)
!           hash_add(&vimvarht, p->vv_di.di_key);
        if (p->vv_flags & VV_COMPAT)
            // add to compat scope dict
!           hash_add(&compat_hashtab, p->vv_di.di_key);
      }
      set_vim_var_nr(VV_VERSION, VIM_VERSION_100);
      set_vim_var_nr(VV_VERSIONLONG, VIM_VERSION_100 * 10000 + highest_patch());
--- 217,226 ----
  
        // add to v: scope dict, unless the value is not always available
        if (p->vv_tv_type != VAR_UNKNOWN)
!           hash_add(&vimvarht, p->vv_di.di_key, "initialization");
        if (p->vv_flags & VV_COMPAT)
            // add to compat scope dict
!           hash_add(&compat_hashtab, p->vv_di.di_key, "initialization");
      }
      set_vim_var_nr(VV_VERSION, VIM_VERSION_100);
      set_vim_var_nr(VV_VERSIONLONG, VIM_VERSION_100 * 10000 + highest_patch());
***************
*** 562,568 ****
      *save_tv = vimvars[idx].vv_tv;
      vimvars[idx].vv_str = NULL;  // don't free it now
      if (vimvars[idx].vv_tv_type == VAR_UNKNOWN)
!       hash_add(&vimvarht, vimvars[idx].vv_di.di_key);
  }
  
  /*
--- 562,568 ----
      *save_tv = vimvars[idx].vv_tv;
      vimvars[idx].vv_str = NULL;  // don't free it now
      if (vimvars[idx].vv_tv_type == VAR_UNKNOWN)
!       hash_add(&vimvarht, vimvars[idx].vv_di.di_key, "prepare vimvar");
  }
  
  /*
***************
*** 582,588 ****
        if (HASHITEM_EMPTY(hi))
            internal_error("restore_vimvar()");
        else
!           hash_remove(&vimvarht, hi);
      }
  }
  
--- 582,588 ----
        if (HASHITEM_EMPTY(hi))
            internal_error("restore_vimvar()");
        else
!           hash_remove(&vimvarht, hi, "restore vimvar");
      }
  }
  
***************
*** 1380,1385 ****
--- 1380,1388 ----
      int               todo;
      char_u    buf[IOSIZE];
  
+     int save_ht_flags = ht->ht_flags;
+     ht->ht_flags |= HTFLAGS_FROZEN;
+ 
      todo = (int)ht->ht_used;
      for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
      {
***************
*** 1399,1404 ****
--- 1402,1409 ----
                list_one_var(di, prefix, first);
        }
      }
+ 
+     ht->ht_flags = save_ht_flags;
  }
  
  /*
***************
*** 2008,2014 ****
        listitem_remove(lp->ll_list, lp->ll_li);
      else
        // unlet a Dictionary item.
!       dictitem_remove(lp->ll_dict, lp->ll_di);
  
      return ret;
  }
--- 2013,2019 ----
        listitem_remove(lp->ll_list, lp->ll_li);
      else
        // unlet a Dictionary item.
!       dictitem_remove(lp->ll_dict, lp->ll_di, "unlet");
  
      return ret;
  }
***************
*** 2095,2101 ****
            di = HI2DI(hi);
            if (var_check_fixed(di->di_flags, name, FALSE)
                    || var_check_ro(di->di_flags, name, FALSE)
!                   || value_check_lock(d->dv_lock, name, FALSE))
                return FAIL;
  
            delete_var(ht, hi);
--- 2100,2107 ----
            di = HI2DI(hi);
            if (var_check_fixed(di->di_flags, name, FALSE)
                    || var_check_ro(di->di_flags, name, FALSE)
!                   || value_check_lock(d->dv_lock, name, FALSE)
!                   || check_hashtab_frozen(ht, "unlet"))
                return FAIL;
  
            delete_var(ht, hi);
***************
*** 3554,3562 ****
  {
      dictitem_T        *di = HI2DI(hi);
  
!     hash_remove(ht, hi);
!     clear_tv(&di->di_tv);
!     vim_free(di);
  }
  
  /*
--- 3560,3570 ----
  {
      dictitem_T        *di = HI2DI(hi);
  
!     if (hash_remove(ht, hi, "delete variable") == OK)
!     {
!       clear_tv(&di->di_tv);
!       vim_free(di);
!     }
  }
  
  /*
***************
*** 3895,3900 ****
--- 3903,3911 ----
            goto failed;
        }
  
+       if (check_hashtab_frozen(ht, "add variable"))
+           goto failed;
+ 
        // Can't add "v:" or "a:" variable.
        if (ht == &vimvarht || ht == get_funccal_args_ht())
        {
***************
*** 3913,3919 ****
        if (di == NULL)
            goto failed;
        STRCPY(di->di_key, varname);
!       if (hash_add(ht, DI2HIKEY(di)) == FAIL)
        {
            vim_free(di);
            goto failed;
--- 3924,3930 ----
        if (di == NULL)
            goto failed;
        STRCPY(di->di_key, varname);
!       if (hash_add(ht, DI2HIKEY(di), "add variable") == FAIL)
        {
            vim_free(di);
            goto failed;
*** ../vim-9.0.0948/src/hashtab.c       2022-02-03 13:02:32.000000000 +0000
--- src/hashtab.c       2022-11-25 16:22:18.337806451 +0000
***************
*** 71,76 ****
--- 71,90 ----
  }
  
  /*
+  * If "ht->ht_flags" has HTFLAGS_FROZEN then give an error message using
+  * "command" and return TRUE.
+  */
+     int
+ check_hashtab_frozen(hashtab_T *ht, char *command)
+ {
+     if ((ht->ht_flags & HTFLAGS_FROZEN) == 0)
+       return FALSE;
+ 
+     semsg(_(e_not_allowed_to_add_or_remove_entries_str), command);
+     return TRUE;
+ }
+ 
+ /*
   * Free the array of a hash table.  Does not free the items it contains!
   * If "ht" is not freed then you should call hash_init() next!
   */
***************
*** 201,214 ****
  
  /*
   * Add item with key "key" to hashtable "ht".
   * Returns FAIL when out of memory or the key is already present.
   */
      int
! hash_add(hashtab_T *ht, char_u *key)
  {
      hash_T    hash = hash_hash(key);
      hashitem_T        *hi;
  
      hi = hash_lookup(ht, key, hash);
      if (!HASHITEM_EMPTY(hi))
      {
--- 215,231 ----
  
  /*
   * Add item with key "key" to hashtable "ht".
+  * "command" is used for the error message when the hashtab if frozen.
   * Returns FAIL when out of memory or the key is already present.
   */
      int
! hash_add(hashtab_T *ht, char_u *key, char *command)
  {
      hash_T    hash = hash_hash(key);
      hashitem_T        *hi;
  
+     if (check_hashtab_frozen(ht, command))
+       return FAIL;
      hi = hash_lookup(ht, key, hash);
      if (!HASHITEM_EMPTY(hi))
      {
***************
*** 232,238 ****
      hash_T    hash)
  {
      // If resizing failed before and it fails again we can't add an item.
!     if (ht->ht_error && hash_may_resize(ht, 0) == FAIL)
        return FAIL;
  
      ++ht->ht_used;
--- 249,255 ----
      hash_T    hash)
  {
      // If resizing failed before and it fails again we can't add an item.
!     if ((ht->ht_flags & HTFLAGS_ERROR) && hash_may_resize(ht, 0) == FAIL)
        return FAIL;
  
      ++ht->ht_used;
***************
*** 266,280 ****
  /*
   * Remove item "hi" from  hashtable "ht".  "hi" must have been obtained with
   * hash_lookup().
   * The caller must take care of freeing the item itself.
   */
!     void
! hash_remove(hashtab_T *ht, hashitem_T *hi)
  {
      --ht->ht_used;
      ++ht->ht_changed;
      hi->hi_key = HI_KEY_REMOVED;
      hash_may_resize(ht, 0);
  }
  
  /*
--- 283,301 ----
  /*
   * Remove item "hi" from  hashtable "ht".  "hi" must have been obtained with
   * hash_lookup().
+  * "command" is used for the error message when the hashtab if frozen.
   * The caller must take care of freeing the item itself.
   */
!     int
! hash_remove(hashtab_T *ht, hashitem_T *hi, char *command)
  {
+     if (check_hashtab_frozen(ht, command))
+       return FAIL;
      --ht->ht_used;
      ++ht->ht_changed;
      hi->hi_key = HI_KEY_REMOVED;
      hash_may_resize(ht, 0);
+     return OK;
  }
  
  /*
***************
*** 407,417 ****
        if (newarray == NULL)
        {
            // Out of memory.  When there are NULL items still return OK.
!           // Otherwise set ht_error, because lookup may result in a hang if
!           // we add another item.
            if (ht->ht_filled < ht->ht_mask)
                return OK;
!           ht->ht_error = TRUE;
            return FAIL;
        }
        oldarray = ht->ht_array;
--- 428,438 ----
        if (newarray == NULL)
        {
            // Out of memory.  When there are NULL items still return OK.
!           // Otherwise set ht_flags to HTFLAGS_ERROR, because lookup may
!           // result in a hang if we add another item.
            if (ht->ht_filled < ht->ht_mask)
                return OK;
!           ht->ht_flags |= HTFLAGS_ERROR;
            return FAIL;
        }
        oldarray = ht->ht_array;
***************
*** 453,459 ****
      ht->ht_mask = newmask;
      ht->ht_filled = ht->ht_used;
      ++ht->ht_changed;
!     ht->ht_error = FALSE;
  
      return OK;
  }
--- 474,480 ----
      ht->ht_mask = newmask;
      ht->ht_filled = ht->ht_used;
      ++ht->ht_changed;
!     ht->ht_flags &= ~HTFLAGS_ERROR;
  
      return OK;
  }
*** ../vim-9.0.0948/src/proto/hashtab.pro       2022-06-27 23:15:08.000000000 
+0100
--- src/proto/hashtab.pro       2022-11-25 16:22:46.221719381 +0000
***************
*** 1,13 ****
  /* hashtab.c */
  void hash_init(hashtab_T *ht);
  void hash_clear(hashtab_T *ht);
  void hash_clear_all(hashtab_T *ht, int off);
  hashitem_T *hash_find(hashtab_T *ht, char_u *key);
  hashitem_T *hash_lookup(hashtab_T *ht, char_u *key, hash_T hash);
  void hash_debug_results(void);
! int hash_add(hashtab_T *ht, char_u *key);
  int hash_add_item(hashtab_T *ht, hashitem_T *hi, char_u *key, hash_T hash);
! void hash_remove(hashtab_T *ht, hashitem_T *hi);
  void hash_lock(hashtab_T *ht);
  void hash_lock_size(hashtab_T *ht, int size);
  void hash_unlock(hashtab_T *ht);
--- 1,14 ----
  /* hashtab.c */
  void hash_init(hashtab_T *ht);
+ int check_hashtab_frozen(hashtab_T *ht, char *command);
  void hash_clear(hashtab_T *ht);
  void hash_clear_all(hashtab_T *ht, int off);
  hashitem_T *hash_find(hashtab_T *ht, char_u *key);
  hashitem_T *hash_lookup(hashtab_T *ht, char_u *key, hash_T hash);
  void hash_debug_results(void);
! int hash_add(hashtab_T *ht, char_u *key, char *command);
  int hash_add_item(hashtab_T *ht, hashitem_T *hi, char_u *key, hash_T hash);
! int hash_remove(hashtab_T *ht, hashitem_T *hi, char *command);
  void hash_lock(hashtab_T *ht);
  void hash_lock_size(hashtab_T *ht, int size);
  void hash_unlock(hashtab_T *ht);
*** ../vim-9.0.0948/src/if_lua.c        2022-09-17 21:07:52.099993159 +0100
--- src/if_lua.c        2022-11-25 16:16:29.083198869 +0000
***************
*** 1150,1156 ****
      if (lua_isnil(L, 3)) // remove?
      {
        hashitem_T *hi = hash_find(&d->dv_hashtab, di->di_key);
!       hash_remove(&d->dv_hashtab, hi);
        dictitem_free(di);
      }
      else
--- 1150,1156 ----
      if (lua_isnil(L, 3)) // remove?
      {
        hashitem_T *hi = hash_find(&d->dv_hashtab, di->di_key);
!       hash_remove(&d->dv_hashtab, hi, "Lua new index");
        dictitem_free(di);
      }
      else
***************
*** 1838,1846 ****
        if (di == NULL)
            // Doesn't exist, nothing to do
            return 0;
!       else
!           // Delete the entry
!           dictitem_remove(dict, di);
      }
      else
      {
--- 1838,1845 ----
        if (di == NULL)
            // Doesn't exist, nothing to do
            return 0;
!       // Delete the entry
!       dictitem_remove(dict, di, "Lua delete variable");
      }
      else
      {
*** ../vim-9.0.0948/src/if_ruby.c       2022-09-22 16:11:47.281127583 +0100
--- src/if_ruby.c       2022-11-25 15:36:19.674531651 +0000
***************
*** 1799,1805 ****
      if (di == NULL || ruby_convert_to_vim_value(val, &di->di_tv) != OK
                                                     || dict_add(d, di) != OK)
      {
!       d->dv_hashtab.ht_error = TRUE;
        return ST_STOP;
      }
      return ST_CONTINUE;
--- 1799,1805 ----
      if (di == NULL || ruby_convert_to_vim_value(val, &di->di_tv) != OK
                                                     || dict_add(d, di) != OK)
      {
!       d->dv_hashtab.ht_flags |= HTFLAGS_ERROR;
        return ST_STOP;
      }
      return ST_CONTINUE;
***************
*** 1879,1885 ****
                    return FAIL;
  
                rb_hash_foreach(val, convert_hash2dict, (VALUE)d);
!               if (d->dv_hashtab.ht_error)
                {
                    dict_unref(d);
                    return FAIL;
--- 1879,1885 ----
                    return FAIL;
  
                rb_hash_foreach(val, convert_hash2dict, (VALUE)d);
!               if (d->dv_hashtab.ht_flags & HTFLAGS_ERROR)
                {
                    dict_unref(d);
                    return FAIL;
*** ../vim-9.0.0948/src/if_py_both.h    2022-09-17 21:07:52.103993150 +0100
--- src/if_py_both.h    2022-11-25 16:14:12.627105898 +0000
***************
*** 1768,1774 ****
            return NULL;
        }
  
!       hash_remove(&dict->dv_hashtab, hi);
        dictitem_free(di);
      }
  
--- 1768,1774 ----
            return NULL;
        }
  
!       hash_remove(&dict->dv_hashtab, hi, "Python remove variable");
        dictitem_free(di);
      }
  
***************
*** 1893,1899 ****
            return -1;
        }
        hi = hash_find(&dict->dv_hashtab, di->di_key);
!       hash_remove(&dict->dv_hashtab, hi);
        dictitem_free(di);
        Py_XDECREF(todecref);
        return 0;
--- 1893,1899 ----
            return -1;
        }
        hi = hash_find(&dict->dv_hashtab, di->di_key);
!       hash_remove(&dict->dv_hashtab, hi, "Python remove item");
        dictitem_free(di);
        Py_XDECREF(todecref);
        return 0;
***************
*** 2194,2200 ****
        return NULL;
      }
  
!     hash_remove(&self->dict->dv_hashtab, hi);
      dictitem_free(di);
  
      return ret;
--- 2194,2200 ----
        return NULL;
      }
  
!     hash_remove(&self->dict->dv_hashtab, hi, "Python pop item");
      dictitem_free(di);
  
      return ret;
*** ../vim-9.0.0948/src/spellfile.c     2022-11-02 13:30:37.538314551 +0000
--- src/spellfile.c     2022-11-25 15:58:10.438078816 +0000
***************
*** 2643,2649 ****
                        smsg(_("Affix also used for 
BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s line %d: %s"),
                                                       fname, lnum, items[1]);
                    STRCPY(cur_aff->ah_key, items[1]);
!                   hash_add(tp, cur_aff->ah_key);
  
                    cur_aff->ah_combine = (*items[2] == 'Y');
                }
--- 2643,2649 ----
                        smsg(_("Affix also used for 
BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s line %d: %s"),
                                                       fname, lnum, items[1]);
                    STRCPY(cur_aff->ah_key, items[1]);
!                   hash_add(tp, cur_aff->ah_key, "spelling");
  
                    cur_aff->ah_combine = (*items[2] == 'Y');
                }
***************
*** 2994,3000 ****
                        p = vim_strsave(items[i]);
                        if (p == NULL)
                            break;
!                       hash_add(&spin->si_commonwords, p);
                    }
                }
            }
--- 2994,3000 ----
                        p = vim_strsave(items[i]);
                        if (p == NULL)
                            break;
!                       hash_add(&spin->si_commonwords, p, "spelling");
                    }
                }
            }
***************
*** 3312,3318 ****
                        id = spin->si_newcompID--;
                    } while (vim_strchr((char_u *)"/?*+[]\\-^", id) != NULL);
                    ci->ci_newID = id;
!                   hash_add(&aff->af_comp, ci->ci_key);
                }
                *tp++ = id;
            }
--- 3312,3318 ----
                        id = spin->si_newcompID--;
                    } while (vim_strchr((char_u *)"/?*+[]\\-^", id) != NULL);
                    ci->ci_newID = id;
!                   hash_add(&aff->af_comp, ci->ci_key, "spelling");
                }
                *tp++ = id;
            }
*** ../vim-9.0.0948/src/sign.c  2022-09-02 15:15:11.063569185 +0100
--- src/sign.c  2022-11-25 16:11:32.590988048 +0000
***************
*** 126,132 ****
        if (group->sg_refcount == 0)
        {
            // All the signs in this group are removed
!           hash_remove(&sg_table, hi);
            vim_free(group);
        }
      }
--- 126,132 ----
        if (group->sg_refcount == 0)
        {
            // All the signs in this group are removed
!           hash_remove(&sg_table, hi, "sign remove");
            vim_free(group);
        }
      }
*** ../vim-9.0.0948/src/syntax.c        2022-10-14 17:04:05.891675444 +0100
--- src/syntax.c        2022-11-25 16:11:54.767005038 +0000
***************
*** 4339,4345 ****
                    if (kp_prev == NULL)
                    {
                        if (kp_next == NULL)
!                           hash_remove(ht, hi);
                        else
                            hi->hi_key = KE2HIKEY(kp_next);
                    }
--- 4339,4345 ----
                    if (kp_prev == NULL)
                    {
                        if (kp_next == NULL)
!                           hash_remove(ht, hi, "syntax clear keyword");
                        else
                            hi->hi_key = KE2HIKEY(kp_next);
                    }
*** ../vim-9.0.0948/src/terminal.c      2022-11-24 14:05:15.526236452 +0000
--- src/terminal.c      2022-11-25 15:58:31.802115403 +0000
***************
*** 1020,1026 ****
        char *hash_key = alloc(NUMBUFLEN);
  
        vim_snprintf(hash_key, NUMBUFLEN, "%d", bufnr);
!       hash_add(terminal_bufs, (char_u *)hash_key);
      }
  
      return put_eol(fd);
--- 1020,1026 ----
        char *hash_key = alloc(NUMBUFLEN);
  
        vim_snprintf(hash_key, NUMBUFLEN, "%d", bufnr);
!       hash_add(terminal_bufs, (char_u *)hash_key, "terminal session");
      }
  
      return put_eol(fd);
*** ../vim-9.0.0948/src/textprop.c      2022-11-02 13:30:37.542314565 +0000
--- src/textprop.c      2022-11-25 16:12:27.035029368 +0000
***************
*** 1789,1795 ****
            }
            hash_init(*htp);
        }
!       hash_add(*htp, PT2HIKEY(prop));
      }
      else
      {
--- 1789,1795 ----
            }
            hash_init(*htp);
        }
!       hash_add(*htp, PT2HIKEY(prop), "prop type");
      }
      else
      {
***************
*** 1924,1930 ****
            ht = buf->b_proptypes;
            VIM_CLEAR(buf->b_proparray);
        }
!       hash_remove(ht, hi);
        vim_free(prop);
      }
  }
--- 1924,1930 ----
            ht = buf->b_proptypes;
            VIM_CLEAR(buf->b_proparray);
        }
!       hash_remove(ht, hi, "prop type delete");
        vim_free(prop);
      }
  }
*** ../vim-9.0.0948/src/vim9execute.c   2022-11-02 13:30:37.542314565 +0000
--- src/vim9execute.c   2022-11-25 16:16:58.735168936 +0000
***************
*** 2366,2372 ****
                                                  NULL, FALSE))
                    status = FAIL;
                else
!                   dictitem_remove(d, di);
            }
        }
      }
--- 2366,2372 ----
                                                  NULL, FALSE))
                    status = FAIL;
                else
!                   dictitem_remove(d, di, "unlet");
            }
        }
      }
*** ../vim-9.0.0948/src/vim9script.c    2022-10-07 17:26:19.019293893 +0100
--- src/vim9script.c    2022-11-25 16:12:57.267051748 +0000
***************
*** 942,948 ****
  
            if (HASHITEM_EMPTY(hi))
                // new variable name
!               hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key);
            else if (sav != NULL)
                // existing name in a new block, append to the list
                sav->sav_next = newsav;
--- 942,949 ----
  
            if (HASHITEM_EMPTY(hi))
                // new variable name
!               hash_add(&si->sn_all_vars.dv_hashtab, newsav->sav_key,
!                                                              "add variable");
            else if (sav != NULL)
                // existing name in a new block, append to the list
                sav->sav_next = newsav;
***************
*** 1033,1039 ****
            else
            {
                if (sav_prev == NULL)
!                   hash_remove(all_ht, all_hi);
                else
                    sav_prev->sav_next = sav->sav_next;
                sv->sv_name = NULL;
--- 1034,1040 ----
            else
            {
                if (sav_prev == NULL)
!                   hash_remove(all_ht, all_hi, "hide variable");
                else
                    sav_prev->sav_next = sav->sav_next;
                sv->sv_name = NULL;
*** ../vim-9.0.0948/src/userfunc.c      2022-11-13 22:13:29.848975595 +0000
--- src/userfunc.c      2022-11-25 16:17:37.018962640 +0000
***************
*** 585,591 ****
      fp->uf_cb_state = state;
  
      set_ufunc_name(fp, name);
!     hash_add(&func_hashtab, UF2HIKEY(fp));
  
      return name;
  }
--- 585,591 ----
      fp->uf_cb_state = state;
  
      set_ufunc_name(fp, name);
!     hash_add(&func_hashtab, UF2HIKEY(fp), "add C function");
  
      return name;
  }
***************
*** 1278,1284 ****
      if (ufunc == NULL)
        goto erret;
      set_ufunc_name(ufunc, name);
!     if (hash_add(&func_hashtab, UF2HIKEY(ufunc)) == FAIL)
        goto erret;
      ufunc->uf_flags = FC_LAMBDA;
      ufunc->uf_refcount = 1;
--- 1278,1284 ----
      if (ufunc == NULL)
        goto erret;
      set_ufunc_name(ufunc, name);
!     if (hash_add(&func_hashtab, UF2HIKEY(ufunc), "add function") == FAIL)
        goto erret;
      ufunc->uf_flags = FC_LAMBDA;
      ufunc->uf_refcount = 1;
***************
*** 1572,1578 ****
        rettv->vval.v_partial = pt;
        rettv->v_type = VAR_PARTIAL;
  
!       hash_add(&func_hashtab, UF2HIKEY(fp));
      }
  
  theend:
--- 1572,1578 ----
        rettv->vval.v_partial = pt;
        rettv->v_type = VAR_PARTIAL;
  
!       hash_add(&func_hashtab, UF2HIKEY(fp), "add lambda");
      }
  
  theend:
***************
*** 2128,2134 ****
  {
      STRCPY(v->di_key, name);
      v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
!     hash_add(&dp->dv_hashtab, DI2HIKEY(v));
      v->di_tv.v_type = VAR_NUMBER;
      v->di_tv.v_lock = VAR_FIXED;
      v->di_tv.vval.v_number = nr;
--- 2128,2134 ----
  {
      STRCPY(v->di_key, name);
      v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
!     hash_add(&dp->dv_hashtab, DI2HIKEY(v), "add variable");
      v->di_tv.v_type = VAR_NUMBER;
      v->di_tv.v_lock = VAR_FIXED;
      v->di_tv.vval.v_number = nr;
***************
*** 2348,2354 ****
            fp->uf_flags |= FC_DEAD;
            return FALSE;
        }
!       hash_remove(&func_hashtab, hi);
        fp->uf_flags |= FC_DELETED;
        return TRUE;
      }
--- 2348,2354 ----
            fp->uf_flags |= FC_DEAD;
            return FALSE;
        }
!       hash_remove(&func_hashtab, hi, "remove function");
        fp->uf_flags |= FC_DELETED;
        return TRUE;
      }
***************
*** 2510,2516 ****
  
      fp->uf_refcount = 1;
      STRCPY(fp->uf_name, global);
!     hash_add(&func_hashtab, UF2HIKEY(fp));
  
      // the referenced dfunc_T is now used one more time
      link_def_function(fp);
--- 2510,2516 ----
  
      fp->uf_refcount = 1;
      STRCPY(fp->uf_name, global);
!     hash_add(&func_hashtab, UF2HIKEY(fp), "copy lambda");
  
      // the referenced dfunc_T is now used one more time
      link_def_function(fp);
***************
*** 2718,2724 ****
        name = v->di_key;
        STRCPY(name, "self");
        v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
!       hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v));
        v->di_tv.v_type = VAR_DICT;
        v->di_tv.v_lock = 0;
        v->di_tv.vval.v_dict = selfdict;
--- 2718,2724 ----
        name = v->di_key;
        STRCPY(name, "self");
        v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
!       hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v), "set self dictionary");
        v->di_tv.v_type = VAR_DICT;
        v->di_tv.v_lock = 0;
        v->di_tv.vval.v_dict = selfdict;
***************
*** 2744,2750 ****
        name = v->di_key;
        STRCPY(name, "000");
        v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
!       hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v));
        v->di_tv.v_type = VAR_LIST;
        v->di_tv.v_lock = VAR_FIXED;
        v->di_tv.vval.v_list = &fc->fc_l_varlist;
--- 2744,2750 ----
        name = v->di_key;
        STRCPY(name, "000");
        v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
!       hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v), "function argument");
        v->di_tv.v_type = VAR_LIST;
        v->di_tv.v_lock = VAR_FIXED;
        v->di_tv.vval.v_list = &fc->fc_l_varlist;
***************
*** 2838,2847 ****
            // Named arguments should be accessed without the "a:" prefix in
            // lambda expressions.  Add to the l: dict.
            copy_tv(&v->di_tv, &v->di_tv);
!           hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v));
        }
        else
!           hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v));
  
        if (ai >= 0 && ai < MAX_FUNC_ARGS)
        {
--- 2838,2847 ----
            // Named arguments should be accessed without the "a:" prefix in
            // lambda expressions.  Add to the l: dict.
            copy_tv(&v->di_tv, &v->di_tv);
!           hash_add(&fc->fc_l_vars.dv_hashtab, DI2HIKEY(v), "local variable");
        }
        else
!           hash_add(&fc->fc_l_avars.dv_hashtab, DI2HIKEY(v), "add variable");
  
        if (ai >= 0 && ai < MAX_FUNC_ARGS)
        {
***************
*** 5060,5066 ****
            hi = hash_find(&func_hashtab, name);
            hi->hi_key = UF2HIKEY(fp);
        }
!       else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL)
        {
            free_fp = TRUE;
            goto erret;
--- 5060,5066 ----
            hi = hash_find(&func_hashtab, name);
            hi->hi_key = UF2HIKEY(fp);
        }
!       else if (hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL)
        {
            free_fp = TRUE;
            goto erret;
***************
*** 5462,5468 ****
        {
            // Delete the dict item that refers to the function, it will
            // invoke func_unref() and possibly delete the function.
!           dictitem_remove(fudi.fd_dict, fudi.fd_di);
        }
        else
        {
--- 5462,5468 ----
        {
            // Delete the dict item that refers to the function, it will
            // invoke func_unref() and possibly delete the function.
!           dictitem_remove(fudi.fd_dict, fudi.fd_di, "delfunction");
        }
        else
        {
*** ../vim-9.0.0948/src/testdir/test_autocmd.vim        2022-11-22 
12:40:44.066427878 +0000
--- src/testdir/test_autocmd.vim        2022-11-25 16:26:19.629169272 +0000
***************
*** 2326,2331 ****
--- 2326,2353 ----
    call StopVimInTerminal(buf)
  endfunc
  
+ func Test_autocmd_CmdlineLeave_unlet()
+   CheckRunVimInTerminal
+ 
+   let lines =<< trim END
+       for i in range(1, 999)
+         exe 'let g:var' .. i '=' i
+       endfor
+       au CmdlineLeave : call timer_start(0, {-> execute('unlet g:var990')})
+   END
+   call writefile(lines, 'XleaveUnlet', 'D')
+   let buf = RunVimInTerminal('-S XleaveUnlet', {'rows': 10})
+ 
+   " this was using freed memory
+   call term_sendkeys(buf, ":let g:\<CR>")
+   call TermWait(buf, 50)
+   call term_sendkeys(buf, "G")
+   call TermWait(buf, 50)
+   call term_sendkeys(buf, "\<CR>")  " for the hit-enter prompt
+ 
+   call StopVimInTerminal(buf)
+ endfunc
+ 
  function s:Before_test_dirchanged()
    augroup test_dirchanged
      autocmd!
*** ../vim-9.0.0948/src/version.c       2022-11-25 15:09:30.710402884 +0000
--- src/version.c       2022-11-25 15:29:26.846659184 +0000
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     949,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
143. You dream in pallettes of 216 websafe colors.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            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/20221125163223.81D651C091A%40moolenaar.net.

Raspunde prin e-mail lui