patch 9.0.0949: crash when unletting a variable while listing variables

Problem:    Crash when unletting a variable while listing variables.
Solution:   Disallow changing a hashtable while going over the entries.
            (closes #11435)
diff --git a/src/dict.c b/src/dict.c
index ffd5d38..1f34b8a 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -122,6 +122,9 @@
     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,7 +135,7 @@
 	    // Remove the item before deleting it, just in case there is
 	    // something recursive causing trouble.
 	    di = HI2DI(hi);
-	    hash_remove(ht, hi);
+	    hash_remove(ht, hi, "clear dict");
 	    dictitem_free(di);
 	    --todo;
 	}
@@ -256,9 +259,10 @@
 
 /*
  * 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)
+dictitem_remove(dict_T *dict, dictitem_T *item, char *command)
 {
     hashitem_T	*hi;
 
@@ -266,7 +270,7 @@
     if (HASHITEM_EMPTY(hi))
 	internal_error("dictitem_remove()");
     else
-	hash_remove(&dict->dv_hashtab, hi);
+	hash_remove(&dict->dv_hashtab, hi, command);
     dictitem_free(item);
 }
 
@@ -375,7 +379,7 @@
 {
     if (dict_wrong_func_name(d, &item->di_tv, item->di_key))
 	return FAIL;
-    return hash_add(&d->dv_hashtab, item->di_key);
+    return hash_add(&d->dv_hashtab, item->di_key, "add to dictionary");
 }
 
 /*
@@ -1094,14 +1098,21 @@
     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;
 
-    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)
     {
@@ -1126,7 +1137,7 @@
 		    // 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);
+			hash_remove(&d2->dv_hashtab, hi2, "extend");
 		}
 		else
 		{
@@ -1406,7 +1417,7 @@
 		if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
 			|| var_check_ro(di->di_flags, arg_errmsg, TRUE))
 		    break;
-		dictitem_remove(d, di);
+		dictitem_remove(d, di, "filter");
 	    }
 	}
     }
@@ -1453,7 +1464,7 @@
 
     *rettv = di->di_tv;
     init_tv(&di->di_tv);
-    dictitem_remove(d, di);
+    dictitem_remove(d, di, "remove()");
 }
 
 typedef enum {