diff options
| author | b-r-o-c-k <brockmammen@gmail.com> | 2018-04-14 14:17:51 -0500 | 
|---|---|---|
| committer | b-r-o-c-k <brockmammen@gmail.com> | 2018-04-14 14:17:51 -0500 | 
| commit | ad999eaa775d7d4b0cacedb30c6ea3a0ee699a6f (patch) | |
| tree | 92de2079e80f5f289dd87a54af123cb7d90c3058 /src/nvim/eval.c | |
| parent | 78bc52ea5397c092d01cd08296fe1dc85d998329 (diff) | |
| parent | ef4feab0e75be19c5f41d70a001db980b72090f5 (diff) | |
| download | rneovim-ad999eaa775d7d4b0cacedb30c6ea3a0ee699a6f.tar.gz rneovim-ad999eaa775d7d4b0cacedb30c6ea3a0ee699a6f.tar.bz2 rneovim-ad999eaa775d7d4b0cacedb30c6ea3a0ee699a6f.zip  | |
Merge branch 'master' into s-dash-stdin
Diffstat (limited to 'src/nvim/eval.c')
| -rw-r--r-- | src/nvim/eval.c | 1969 | 
1 files changed, 1000 insertions, 969 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 577aa67c60..284185083e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -110,9 +110,6 @@  #define DICT_MAXNEST 100        /* maximum nesting of lists and dicts */ -#define DO_NOT_FREE_CNT 99999   /* refcount for dict or list that should not -                                   be freed. */ -  #define AUTOLOAD_CHAR '#'       /* Character used as separator in autoload                                      function/variable names. */ @@ -218,6 +215,15 @@ static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL};  static int echo_attr = 0;   /* attributes used for ":echo" */ +/// Describe data to return from find_some_match() +typedef enum { +  kSomeMatch,  ///< Data for match(). +  kSomeMatchEnd,  ///< Data for matchend(). +  kSomeMatchList,  ///< Data for matchlist(). +  kSomeMatchStr,  ///< Data for matchstr(). +  kSomeMatchStrPos,  ///< Data for matchstrpos(). +} SomeMatchType; +  /// trans_function_name() flags  typedef enum {    TFN_INT = 1,  ///< May use internal function name @@ -396,7 +402,6 @@ static struct vimvar {    VV(VV_OLDFILES,       "oldfiles",         VAR_LIST, 0),    VV(VV_WINDOWID,       "windowid",         VAR_NUMBER, VV_RO_SBX),    VV(VV_PROGPATH,       "progpath",         VAR_STRING, VV_RO), -  VV(VV_COMMAND_OUTPUT, "command_output",   VAR_STRING, 0),    VV(VV_COMPLETED_ITEM, "completed_item",   VAR_DICT, VV_RO),    VV(VV_OPTION_NEW,     "option_new",       VAR_STRING, VV_RO),    VV(VV_OPTION_OLD,     "option_old",       VAR_STRING, VV_RO), @@ -563,9 +568,9 @@ void eval_init(void)    dict_T *const msgpack_types_dict = tv_dict_alloc();    for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { -    list_T *const type_list = tv_list_alloc(); -    type_list->lv_lock = VAR_FIXED; -    type_list->lv_refcount = 1; +    list_T *const type_list = tv_list_alloc(0); +    tv_list_set_lock(type_list, VAR_FIXED); +    tv_list_ref(type_list);      dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]);      di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX;      di->di_tv = (typval_T) { @@ -586,7 +591,7 @@ void eval_init(void)    dict_T *v_event = tv_dict_alloc();    v_event->dv_lock = VAR_FIXED;    set_vim_var_dict(VV_EVENT, v_event); -  set_vim_var_list(VV_ERRORS, tv_list_alloc()); +  set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown));    set_vim_var_nr(VV_STDERR,   CHAN_STDERR);    set_vim_var_nr(VV_SEARCHFORWARD, 1L);    set_vim_var_nr(VV_HLSEARCH, 1L); @@ -1014,7 +1019,7 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert)        ga_init(&ga, (int)sizeof(char), 80);        if (tv.vval.v_list != NULL) {          tv_list_join(&ga, tv.vval.v_list, "\n"); -        if (tv.vval.v_list->lv_len > 0) { +        if (tv_list_len(tv.vval.v_list) > 0) {            ga_append(&ga, NL);          }        } @@ -1102,10 +1107,11 @@ static void restore_vimvar(int idx, typval_T *save_tv)    vimvars[idx].vv_tv = *save_tv;    if (vimvars[idx].vv_type == VAR_UNKNOWN) {      hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key); -    if (HASHITEM_EMPTY(hi)) -      EMSG2(_(e_intern2), "restore_vimvar()"); -    else +    if (HASHITEM_EMPTY(hi)) { +      internal_error("restore_vimvar()"); +    } else {        hash_remove(&vimvarht, hi); +    }    }  } @@ -1143,27 +1149,31 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr)    return list;  } -/* - * "list" is supposed to contain two items: a word and a number.  Return the - * word in "pp" and the number as the return value. - * Return -1 if anything isn't right. - * Used to get the good word and score from the eval_spell_expr() result. - */ -int get_spellword(list_T *list, const char **pp) +/// Get spell word from an entry from spellsuggest=expr: +/// +/// Entry in question is supposed to be a list (to be checked by the caller) +/// with two items: a word and a score represented as an unsigned number +/// (whether it actually is unsigned is not checked). +/// +/// Used to get the good word and score from the eval_spell_expr() result. +/// +/// @param[in]  list  List to get values from. +/// @param[out]  ret_word  Suggested word. Not initialized if return value is +///                        -1. +/// +/// @return -1 in case of error, score otherwise. +int get_spellword(list_T *const list, const char **ret_word)  { -  listitem_T  *li; - -  li = list->lv_first; -  if (li == NULL) { +  if (tv_list_len(list) != 2) { +    EMSG(_("E5700: Expression from 'spellsuggest' must yield lists with " +           "exactly two values"));      return -1;    } -  *pp = tv_get_string(&li->li_tv); - -  li = li->li_next; -  if (li == NULL) { +  *ret_word = tv_list_find_str(list, 0); +  if (*ret_word == NULL) {      return -1;    } -  return tv_get_number(&li->li_tv); +  return tv_list_find_nr(list, -1, NULL);  } @@ -1269,7 +1279,7 @@ varnumber_T call_func_retnr(char_u *func, int argc,  char *call_func_retstr(const char *const func, const int argc,                         const char_u *const *const argv,                         const bool safe) -  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC +  FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC  {    typval_T rettv;    // All arguments are passed as strings, no conversion to number. @@ -1504,9 +1514,6 @@ ex_let_vars (  )  {    char_u *arg = arg_start; -  list_T      *l; -  int i; -  listitem_T  *item;    typval_T ltv;    if (*arg != '[') { @@ -1518,46 +1525,52 @@ ex_let_vars (      return OK;    } -  /* -   * ":let [v1, v2] = list" or ":for [v1, v2] in listlist" -   */ -  if (tv->v_type != VAR_LIST || (l = tv->vval.v_list) == NULL) { +  // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" +  if (tv->v_type != VAR_LIST) {      EMSG(_(e_listreq));      return FAIL;    } +  list_T *const l = tv->vval.v_list; -  i = tv_list_len(l); -  if (semicolon == 0 && var_count < i) { +  const int len = tv_list_len(l); +  if (semicolon == 0 && var_count < len) {      EMSG(_("E687: Less targets than List items"));      return FAIL;    } -  if (var_count - semicolon > i) { +  if (var_count - semicolon > len) {      EMSG(_("E688: More targets than List items"));      return FAIL;    } +  // List l may actually be NULL, but it should fail with E688 or even earlier +  // if you try to do ":let [] = v:_null_list". +  assert(l != NULL); -  item = l->lv_first; +  listitem_T *item = tv_list_first(l); +  size_t rest_len = tv_list_len(l);    while (*arg != ']') {      arg = skipwhite(arg + 1); -    arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]", nextchars); -    item = item->li_next; -    if (arg == NULL) +    arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]", +                     nextchars); +    if (arg == NULL) {        return FAIL; +    } +    rest_len--; +    item = TV_LIST_ITEM_NEXT(l, item);      arg = skipwhite(arg);      if (*arg == ';') {        /* Put the rest of the list (may be empty) in the var after ';'.         * Create a new list for this. */ -      l = tv_list_alloc(); +      list_T *const rest_list = tv_list_alloc(rest_len);        while (item != NULL) { -        tv_list_append_tv(l, &item->li_tv); -        item = item->li_next; +        tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); +        item = TV_LIST_ITEM_NEXT(l, item);        }        ltv.v_type = VAR_LIST; -      ltv.v_lock = 0; -      ltv.vval.v_list = l; -      l->lv_refcount = 1; +      ltv.v_lock = VAR_UNLOCKED; +      ltv.vval.v_list = rest_list; +      tv_list_ref(rest_list);        arg = ex_let_one(skipwhite(arg + 1), <v, false,                         (char_u *)"]", nextchars); @@ -1567,7 +1580,7 @@ ex_let_vars (        }        break;      } else if (*arg != ',' && *arg != ']') { -      EMSG2(_(e_intern2), "ex_let_vars()"); +      internal_error("ex_let_vars()");        return FAIL;      }    } @@ -2072,6 +2085,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,     * Loop until no more [idx] or .key is following.     */    lp->ll_tv = &v->di_tv; +  var1.v_type = VAR_UNKNOWN; +  var2.v_type = VAR_UNKNOWN;    while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) {      if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL)          && !(lp->ll_tv->v_type == VAR_DICT @@ -2122,9 +2137,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,            if (!quiet) {              EMSG(_(e_dictrange));            } -          if (!empty1) { -            tv_clear(&var1); -          } +          tv_clear(&var1);            return NULL;          }          if (rettv != NULL && (rettv->v_type != VAR_LIST @@ -2132,9 +2145,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,            if (!quiet) {              emsgf(_("E709: [:] requires a List value"));            } -          if (!empty1) { -            tv_clear(&var1); -          } +          tv_clear(&var1);            return NULL;          }          p = skipwhite(p + 1); @@ -2143,16 +2154,12 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,          } else {            lp->ll_empty2 = false;            if (eval1(&p, &var2, true) == FAIL) {  // Recursive! -            if (!empty1) { -              tv_clear(&var1); -            } +            tv_clear(&var1);              return NULL;            }            if (!tv_check_str(&var2)) {              // Not a number or string. -            if (!empty1) { -              tv_clear(&var1); -            } +            tv_clear(&var1);              tv_clear(&var2);              return NULL;            } @@ -2165,12 +2172,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,          if (!quiet) {            emsgf(_(e_missbrac));          } -        if (!empty1) { -          tv_clear(&var1); -        } -        if (lp->ll_range && !lp->ll_empty2) { -          tv_clear(&var2); -        } +        tv_clear(&var1); +        tv_clear(&var2);          return NULL;        } @@ -2224,9 +2227,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,            if (!quiet) {              emsgf(_(e_dictkey), key);            } -          if (len == -1) { -            tv_clear(&var1); -          } +          tv_clear(&var1);            return NULL;          }          if (len == -1) { @@ -2234,32 +2235,28 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,          } else {            lp->ll_newkey = vim_strnsave(key, len);          } -        if (len == -1) { -          tv_clear(&var1); -        } +        tv_clear(&var1);          break;        // existing variable, need to check if it can be changed        } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags,                                                            (const char *)name,                                                            (size_t)(p - name))) { -        if (len == -1) { -          tv_clear(&var1); -        } +        tv_clear(&var1);          return NULL;        } -      if (len == -1) { -        tv_clear(&var1); -      } +      tv_clear(&var1);        lp->ll_tv = &lp->ll_di->di_tv;      } else {        // Get the number and item for the only or first index of the List.        if (empty1) {          lp->ll_n1 = 0;        } else { -        lp->ll_n1 = (long)tv_get_number(&var1);  // Is number or string. -        tv_clear(&var1); +        // Is number or string. +        lp->ll_n1 = (long)tv_get_number(&var1);        } +      tv_clear(&var1); +        lp->ll_dict = NULL;        lp->ll_list = lp->ll_tv->vval.v_list;        lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); @@ -2270,9 +2267,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,          }        }        if (lp->ll_li == NULL) { -        if (lp->ll_range && !lp->ll_empty2) { -          tv_clear(&var2); -        } +        tv_clear(&var2);          if (!quiet) {            EMSGN(_(e_listidx), lp->ll_n1);          } @@ -2310,10 +2305,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv,          }        } -      lp->ll_tv = &lp->ll_li->li_tv; +      lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li);      }    } +  tv_clear(&var1);    return p;  } @@ -2375,47 +2371,55 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,      int ll_n1 = lp->ll_n1;      // Check whether any of the list items is locked -    for (listitem_T *ri = rettv->vval.v_list->lv_first; +    for (listitem_T *ri = tv_list_first(rettv->vval.v_list);           ri != NULL && ll_li != NULL; ) { -      if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, +      if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, +                        (const char *)lp->ll_name,                          TV_CSTRING)) {          return;        } -      ri = ri->li_next; +      ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri);        if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) {          break;        } -      ll_li = ll_li->li_next; +      ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li);        ll_n1++;      }      /*       * Assign the List values to the list items.       */ -    for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) { +    for (ri = tv_list_first(rettv->vval.v_list); ri != NULL; ) {        if (op != NULL && *op != '=') { -        eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); +        eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), +                    (const char *)op);        } else { -        tv_clear(&lp->ll_li->li_tv); -        tv_copy(&ri->li_tv, &lp->ll_li->li_tv); +        tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); +        tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li));        } -      ri = ri->li_next; -      if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) +      ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); +      if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) {          break; -      if (lp->ll_li->li_next == NULL) { +      } +      assert(lp->ll_li != NULL); +      if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) {          // Need to add an empty item.          tv_list_append_number(lp->ll_list, 0); -        assert(lp->ll_li->li_next); +        // ll_li may have become invalid after append, don’t use it. +        lp->ll_li = tv_list_last(lp->ll_list);  // Valid again. +      } else { +        lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);        } -      lp->ll_li = lp->ll_li->li_next; -      ++lp->ll_n1; +      lp->ll_n1++;      } -    if (ri != NULL) +    if (ri != NULL) {        EMSG(_("E710: List value has more items than target")); -    else if (lp->ll_empty2 -             ? (lp->ll_li != NULL && lp->ll_li->li_next != NULL) -             : lp->ll_n1 != lp->ll_n2) +    } else if (lp->ll_empty2 +               ? (lp->ll_li != NULL +                  && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) +               : lp->ll_n1 != lp->ll_n2) {        EMSG(_("E711: List value has not enough items")); +    }    } else {      typval_T oldtv = TV_INITIAL_VALUE;      dict_T *dict = lp->ll_dict; @@ -2460,9 +2464,11 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,  notify:      if (watched) {        if (oldtv.v_type == VAR_UNKNOWN) { +        assert(lp->ll_newkey != NULL);          tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL);        } else {          dictitem_T *di = lp->ll_di; +        assert(di->di_key != NULL);          tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv);          tv_clear(&oldtv);        } @@ -2514,7 +2520,7 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)           * list being used in "tv". */          fi->fi_list = l;          tv_list_watch_add(l, &fi->fi_lw); -        fi->fi_lw.lw_item = l->lv_first; +        fi->fi_lw.lw_item = tv_list_first(l);        }      }    } @@ -2532,21 +2538,18 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip)   * Return TRUE when a valid item was found, FALSE when at end of list or   * something wrong.   */ -int next_for_item(void *fi_void, char_u *arg) +bool next_for_item(void *fi_void, char_u *arg)  { -  forinfo_T   *fi = (forinfo_T *)fi_void; -  int result; -  listitem_T  *item; +  forinfo_T *fi = (forinfo_T *)fi_void; -  item = fi->fi_lw.lw_item; -  if (item == NULL) -    result = FALSE; -  else { -    fi->fi_lw.lw_item = item->li_next; -    result = (ex_let_vars(arg, &item->li_tv, TRUE, -                  fi->fi_semicolon, fi->fi_varcount, NULL) == OK); +  listitem_T *item = fi->fi_lw.lw_item; +  if (item == NULL) { +    return false; +  } else { +    fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item); +    return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true, +                        fi->fi_semicolon, fi->fi_varcount, NULL) == OK);    } -  return result;  }  // TODO(ZyX-I): move to eval/ex_cmds @@ -2870,7 +2873,10 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)      }      *name_end = cc;    } else if ((lp->ll_list != NULL -              && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name, +              // ll_list is not NULL when lvalue is not in a list, NULL lists +              // yield E689. +              && tv_check_lock(tv_list_locked(lp->ll_list), +                               (const char *)lp->ll_name,                                 lp->ll_name_len))               || (lp->ll_dict != NULL                   && tv_check_lock(lp->ll_dict->dv_lock, @@ -2878,27 +2884,26 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)                                    lp->ll_name_len))) {      return FAIL;    } else if (lp->ll_range) { -    listitem_T    *li; -    listitem_T *ll_li = lp->ll_li; -    int ll_n1 = lp->ll_n1; - -    while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { -      li = ll_li->li_next; -      if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, +    assert(lp->ll_list != NULL); +    // Delete a range of List items. +    listitem_T *const first_li = lp->ll_li; +    listitem_T *last_li = first_li; +    for (;;) { +      listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); +      if (tv_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, +                        (const char *)lp->ll_name,                          lp->ll_name_len)) {          return false;        } -      ll_li = li; -      ll_n1++; -    } - -    /* Delete a range of List items. */ -    while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { -      li = lp->ll_li->li_next; -      tv_list_item_remove(lp->ll_list, lp->ll_li);        lp->ll_li = li; -      ++lp->ll_n1; +      lp->ll_n1++; +      if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { +        break; +      } else { +        last_li = lp->ll_li; +      }      } +    tv_list_remove_items(lp->ll_list, first_li, last_li);    } else {      if (lp->ll_list != NULL) {        // unlet a List item. @@ -2906,6 +2911,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)      } else {        // unlet a Dictionary item.        dict_T *d = lp->ll_dict; +      assert(d != NULL);        dictitem_T *di = lp->ll_di;        bool watched = tv_dict_is_watched(d);        char *key = NULL; @@ -2960,7 +2966,7 @@ int do_unlet(const char *const name, const size_t name_len, const int forceit)        d = di->di_tv.vval.v_dict;      }      if (d == NULL) { -      EMSG2(_(e_intern2), "do_unlet()"); +      internal_error("do_unlet()");        return FAIL;      }      hashitem_T *hi = hash_find(ht, (const char_u *)varname); @@ -3043,13 +3049,13 @@ static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep,      /* (un)lock a range of List items. */      while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { -      tv_item_lock(&li->li_tv, deep, lock); -      li = li->li_next; -      ++lp->ll_n1; +      tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock); +      li = TV_LIST_ITEM_NEXT(lp->ll_list, li); +      lp->ll_n1++;      }    } else if (lp->ll_list != NULL) {      // (un)lock a List item. -    tv_item_lock(&lp->ll_li->li_tv, deep, lock); +    tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock);    } else {      // (un)lock a Dictionary item.      tv_item_lock(&lp->ll_di->di_tv, deep, lock); @@ -4508,18 +4514,18 @@ eval_index (            if (!empty2 && (n2 < 0 || n2 + 1 < n1)) {              n2 = -1;            } -          l = tv_list_alloc(); +          l = tv_list_alloc(n2 - n1 + 1);            item = tv_list_find(rettv->vval.v_list, n1);            while (n1++ <= n2) { -            tv_list_append_tv(l, &item->li_tv); -            item = item->li_next; +            tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); +            item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item);            }            tv_clear(rettv);            rettv->v_type = VAR_LIST;            rettv->vval.v_list = l; -          l->lv_refcount++; +          tv_list_ref(l);          } else { -          tv_copy(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); +          tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, n1)), &var1);            tv_clear(rettv);            *rettv = var1;          } @@ -4864,35 +4870,34 @@ void partial_unref(partial_T *pt)  static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)  {    list_T      *l = NULL; -  typval_T tv; -  listitem_T  *item;    if (evaluate) { -    l = tv_list_alloc(); +    l = tv_list_alloc(kListLenShouldKnow);    }    *arg = skipwhite(*arg + 1);    while (**arg != ']' && **arg != NUL) { -    if (eval1(arg, &tv, evaluate) == FAIL)      /* recursive! */ +    typval_T tv; +    if (eval1(arg, &tv, evaluate) == FAIL) {  // Recursive!        goto failret; +    }      if (evaluate) { -      item = tv_list_item_alloc(); -      item->li_tv = tv; -      item->li_tv.v_lock = 0; -      tv_list_append(l, item); +      tv.v_lock = VAR_UNLOCKED; +      tv_list_append_owned_tv(l, tv);      } -    if (**arg == ']') +    if (**arg == ']') {        break; +    }      if (**arg != ',') { -      EMSG2(_("E696: Missing comma in List: %s"), *arg); +      emsgf(_("E696: Missing comma in List: %s"), *arg);        goto failret;      }      *arg = skipwhite(*arg + 1);    }    if (**arg != ']') { -    EMSG2(_("E697: Missing end of List ']': %s"), *arg); +    emsgf(_("E697: Missing end of List ']': %s"), *arg);  failret:      if (evaluate) {        tv_list_free(l); @@ -4904,7 +4909,7 @@ failret:    if (evaluate) {      rettv->v_type = VAR_LIST;      rettv->vval.v_list = l; -    ++l->lv_refcount; +    tv_list_ref(l);    }    return OK; @@ -5172,6 +5177,8 @@ bool garbage_collect(bool testing)      ABORTING(set_ref_list)(sub.additional_elements, copyID);    } +  ABORTING(set_ref_in_quickfix)(copyID); +    bool did_free = false;    if (!abort) {      // 2. Free lists and dictionaries that are not referenced. @@ -5239,8 +5246,8 @@ static int free_unref_items(int copyID)    // But don't free a list that has a watcher (used in a for loop), these    // are not referenced anywhere.    for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) { -    if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) -        && ll->lv_watch == NULL) { +    if ((tv_list_copyid(ll) & COPYID_MASK) != (copyID & COPYID_MASK) +        && !tv_list_has_watchers(ll)) {        // Free the List and ordinary items it contains, but don't recurse        // into Lists and Dictionaries, they will be in the list of dicts        // or list of lists. @@ -5260,7 +5267,7 @@ static int free_unref_items(int copyID)    for (ll = gc_first_list; ll != NULL; ll = ll_next) {      ll_next = ll->lv_used_next;      if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) -        && ll->lv_watch == NULL) { +        && !tv_list_has_watchers(ll)) {        // Free the List and ordinary items it contains, but don't recurse        // into Lists and Dictionaries, they will be in the list of dicts        // or list of lists. @@ -5325,15 +5332,16 @@ bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack)    list_T *cur_l = l;    for (;;) { -    if (!abort) { -      // Mark each item in the list.  If the item contains a hashtab -      // it is added to ht_stack, if it contains a list it is added to -      // list_stack. -      for (listitem_T *li = cur_l->lv_first; !abort && li != NULL; -           li = li->li_next) { -        abort = set_ref_in_item(&li->li_tv, copyID, ht_stack, &list_stack); +    // Mark each item in the list.  If the item contains a hashtab +    // it is added to ht_stack, if it contains a list it is added to +    // list_stack. +    TV_LIST_ITER(cur_l, li, { +      if (abort) { +        break;        } -    } +      abort = set_ref_in_item(TV_LIST_ITEM_TV(li), copyID, ht_stack, +                              &list_stack); +    });      if (list_stack == NULL) {        break; @@ -5928,6 +5936,14 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate)  }  #ifdef INCLUDE_GENERATED_DECLARATIONS + +#ifdef _MSC_VER +// This prevents MSVC from replacing the functions with intrinsics, +// and causing errors when trying to get their addresses in funcs.generated.h +#pragma function (ceil) +#pragma function (floor) +#endif +  # include "funcs.generated.h"  #endif @@ -6530,12 +6546,10 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr)   */  static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  list_T      *l; - -  rettv->vval.v_number = 1;   /* Default: Failed */ +  rettv->vval.v_number = 1;  // Default: failed.    if (argvars[0].v_type == VAR_LIST) { -    if ((l = argvars[0].vval.v_list) != NULL -        && !tv_check_lock(l->lv_lock, "add() argument", TV_TRANSLATE)) { +    list_T *const l = argvars[0].vval.v_list; +    if (!tv_check_lock(tv_list_locked(l), N_("add() argument"), TV_TRANSLATE)) {        tv_list_append_tv(l, &argvars[1]);        tv_copy(&argvars[0], rettv);      } @@ -6586,9 +6600,10 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)        && u_save(lnum, lnum + 1) == OK) {      if (argvars[1].v_type == VAR_LIST) {        l = argvars[1].vval.v_list; -      if (l == NULL) +      if (l == NULL) {          return; -      li = l->lv_first; +      } +      li = tv_list_first(l);      }      for (;; ) {        if (l == NULL) { @@ -6596,7 +6611,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)        } else if (li == NULL) {          break;  // End of list.        } else { -        tv = &li->li_tv;  // Append item from list. +        tv = TV_LIST_ITEM_TV(li);  // Append item from list.        }        const char *const line = tv_get_string_chk(tv);        if (line == NULL) {  // Type error. @@ -6608,7 +6623,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)        if (l == NULL) {          break;        } -      li = li->li_next; +      li = TV_LIST_ITEM_NEXT(l, li);      }      appended_lines_mark(lnum, added); @@ -6661,7 +6676,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)      }      rettv->v_type = VAR_STRING;    } else { -    tv_list_alloc_ret(rettv); +    tv_list_alloc_ret(rettv, ARGCOUNT);      for (idx = 0; idx < ARGCOUNT; idx++) {        tv_list_append_string(rettv->vval.v_list,                              (const char *)alist_name(&ARGLIST[idx]), -1); @@ -6771,7 +6786,7 @@ static void assert_error(garray_T *gap)    if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) {      // Make sure v:errors is a list. -    set_vim_var_list(VV_ERRORS, tv_list_alloc()); +    set_vim_var_list(VV_ERRORS, tv_list_alloc(1));    }    tv_list_append_string(vimvars[VV_ERRORS].vv_list,                          (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); @@ -7247,30 +7262,26 @@ static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, FunPtr fptr)  int func_call(char_u *name, typval_T *args, partial_T *partial,                dict_T *selfdict, typval_T *rettv)  { -  listitem_T  *item;    typval_T argv[MAX_FUNC_ARGS + 1];    int argc = 0;    int dummy;    int r = 0; -  for (item = args->vval.v_list->lv_first; item != NULL; -       item = item->li_next) { +  TV_LIST_ITER(args->vval.v_list, item, {      if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) {        EMSG(_("E699: Too many arguments")); -      break; +      goto func_call_skip_call;      } -    /* Make a copy of each argument.  This is needed to be able to set -     * v_lock to VAR_FIXED in the copy without changing the original list. -     */ -    tv_copy(&item->li_tv, &argv[argc++]); -  } +    // Make a copy of each argument.  This is needed to be able to set +    // v_lock to VAR_FIXED in the copy without changing the original list. +    tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]); +  }); -  if (item == NULL) { -    r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL, -                  curwin->w_cursor.lnum, curwin->w_cursor.lnum, -                  &dummy, true, partial, selfdict); -  } +  r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL, +                curwin->w_cursor.lnum, curwin->w_cursor.lnum, +                &dummy, true, partial, selfdict); +func_call_skip_call:    // Free the arguments.    while (argc > 0) {      tv_clear(&argv[--argc]); @@ -7487,7 +7498,7 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)    if (!undo_allowed())      return; -  if (argvars[1].v_type != VAR_LIST || argvars[1].vval.v_list == NULL) { +  if (argvars[1].v_type != VAR_LIST) {      EMSG(_(e_invarg));      return;    } @@ -7595,7 +7606,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)      long idx;      if ((l = argvars[0].vval.v_list) != NULL) { -      li = l->lv_first; +      li = tv_list_first(l);        if (argvars[2].v_type != VAR_UNKNOWN) {          bool error = false; @@ -7613,9 +7624,11 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)            li = NULL;        } -      for (; li != NULL; li = li->li_next) -        if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE)) -          ++n; +      for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { +        if (tv_equal(TV_LIST_ITEM_TV(li), &argvars[1], ic, false)) { +          n++; +        } +      }      }    } else if (argvars[0].v_type == VAR_DICT) {      int todo; @@ -7933,34 +7946,51 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)    bool n = true;    switch (argvars[0].v_type) { -  case VAR_STRING: -  case VAR_FUNC: -    n = argvars[0].vval.v_string == NULL -        || *argvars[0].vval.v_string == NUL; -    break; -  case VAR_PARTIAL: -    n = false; -    break; -  case VAR_NUMBER: -    n = argvars[0].vval.v_number == 0; -    break; -  case VAR_FLOAT: -    n = argvars[0].vval.v_float == 0.0; -    break; -  case VAR_LIST: -    n = argvars[0].vval.v_list == NULL -        || argvars[0].vval.v_list->lv_first == NULL; -    break; -  case VAR_DICT: -    n = argvars[0].vval.v_dict == NULL -        || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; -    break; -  case VAR_SPECIAL: -    n = argvars[0].vval.v_special != kSpecialVarTrue; -    break; -  case VAR_UNKNOWN: -    EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); -    break; +    case VAR_STRING: +    case VAR_FUNC: { +      n = argvars[0].vval.v_string == NULL +          || *argvars[0].vval.v_string == NUL; +      break; +    } +    case VAR_PARTIAL: { +      n = false; +      break; +    } +    case VAR_NUMBER: { +      n = argvars[0].vval.v_number == 0; +      break; +    } +    case VAR_FLOAT: { +      n = argvars[0].vval.v_float == 0.0; +      break; +    } +    case VAR_LIST: { +      n = (tv_list_len(argvars[0].vval.v_list) == 0); +      break; +    } +    case VAR_DICT: { +      n = (tv_dict_len(argvars[0].vval.v_dict) == 0); +      break; +    } +    case VAR_SPECIAL: { +      // Using switch to get warning if SpecialVarValue receives more values. +      switch (argvars[0].vval.v_special) { +        case kSpecialVarTrue: { +          n = false; +          break; +        } +        case kSpecialVarFalse: +        case kSpecialVarNull: { +          n = true; +          break; +        } +      } +      break; +    } +    case VAR_UNKNOWN: { +      internal_error("f_empty(UNKNOWN)"); +      break; +    }    }    rettv->vval.v_number = n; @@ -8024,17 +8054,22 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr)            && os_can_exe((const char_u *)name, NULL, false)));  } +typedef struct { +  const list_T *const l; +  const listitem_T *li; +} GetListLineCookie; +  static char_u *get_list_line(int c, void *cookie, int indent)  { -  const listitem_T **const p = (const listitem_T **)cookie; -  const listitem_T *item = *p; +  GetListLineCookie *const p = (GetListLineCookie *)cookie; +  const listitem_T *const item = p->li;    if (item == NULL) {      return NULL;    }    char buf[NUMBUFLEN]; -  const char *const s = tv_get_string_buf_chk(&item->li_tv, buf); -  *p = item->li_next; +  const char *const s = tv_get_string_buf_chk(TV_LIST_ITEM_TV(item), buf); +  p->li = TV_LIST_ITEM_NEXT(p->l, item);    return (char_u *)(s == NULL ? NULL : xstrdup(s));  } @@ -8076,11 +8111,14 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)      do_cmdline_cmd(tv_get_string(&argvars[0]));    } else if (argvars[0].vval.v_list != NULL) {      list_T *const list = argvars[0].vval.v_list; -    list->lv_refcount++; -    listitem_T *const item = list->lv_first; -    do_cmdline(NULL, get_list_line, (void *)&item, +    tv_list_ref(list); +    GetListLineCookie cookie = { +      .l = list, +      .li = tv_list_first(list), +    }; +    do_cmdline(NULL, get_list_line, (void *)&cookie,                 DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); -    list->lv_refcount--; +    tv_list_unref(list);    }    msg_silent = save_msg_silent;    emsg_silent = save_emsg_silent; @@ -8088,8 +8126,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)    ga_append(capture_ga, NUL);    rettv->v_type = VAR_STRING; -  rettv->vval.v_string = vim_strsave(capture_ga->ga_data); -  ga_clear(capture_ga); +  rettv->vval.v_string = capture_ga->ga_data;    capture_ga = save_capture_ga;  } @@ -8198,7 +8235,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)      result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL);      emsg_off--;      if (rettv->v_type == VAR_LIST) { -      tv_list_alloc_ret(rettv); +      tv_list_alloc_ret(rettv, (result != NULL));        if (result != NULL) {          tv_list_append_string(rettv->vval.v_list, (const char *)result, -1);        } @@ -8221,8 +8258,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)          rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options,                                           WILD_ALL);        } else { -        tv_list_alloc_ret(rettv);          ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP); +        tv_list_alloc_ret(rettv, xpc.xp_numfiles);          for (int i = 0; i < xpc.xp_numfiles; i++) {            tv_list_append_string(rettv->vval.v_list,                                  (const char *)xpc.xp_files[i], -1); @@ -8239,7 +8276,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)  /// "menu_get(path [, modes])" function  static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, kListLenMayKnow);    int modes = MENU_ALL_MODES;    if (argvars[1].v_type == VAR_STRING) {      const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]); @@ -8262,14 +8299,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)      list_T *const l1 = argvars[0].vval.v_list;      list_T *const l2 = argvars[1].vval.v_list; -    if (l1 == NULL) { -      const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); -      (void)locked; -      assert(locked == true); -    } else if (l2 == NULL) { -      // Do nothing -      tv_copy(&argvars[0], rettv); -    } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, TV_TRANSLATE)) { +    if (!tv_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {        listitem_T *item;        if (argvars[2].v_type != VAR_UNKNOWN) {          before = (long)tv_get_number_chk(&argvars[2], &error); @@ -8277,7 +8307,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)            return;  // Type error; errmsg already given.          } -        if (before == l1->lv_len) { +        if (before == tv_list_len(l1)) {            item = NULL;          } else {            item = tv_list_find(l1, before); @@ -8286,8 +8316,9 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)              return;            }          } -      } else +      } else {          item = NULL; +      }        tv_list_extend(l1, l2, item);        tv_copy(&argvars[0], rettv); @@ -8406,7 +8437,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)    }    if (count < 0) { -    tv_list_alloc_ret(rettv); +    tv_list_alloc_ret(rettv, kListLenUnknown);    }    if (*fname != NUL && !error) { @@ -8439,7 +8470,6 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)  static void filter_map(typval_T *argvars, typval_T *rettv, int map)  {    typval_T    *expr; -  listitem_T  *li, *nli;    list_T      *l = NULL;    dictitem_T  *di;    hashtab_T   *ht; @@ -8457,11 +8487,14 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)    int idx = 0;    if (argvars[0].v_type == VAR_LIST) { +    tv_copy(&argvars[0], rettv);      if ((l = argvars[0].vval.v_list) == NULL -        || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE))) { +        || (!map && tv_check_lock(tv_list_locked(l), arg_errmsg, +                                  TV_TRANSLATE))) {        return;      }    } else if (argvars[0].v_type == VAR_DICT) { +    tv_copy(&argvars[0], rettv);      if ((d = argvars[0].vval.v_dict) == NULL          || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {        return; @@ -8520,18 +8553,21 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)      } else {        vimvars[VV_KEY].vv_type = VAR_NUMBER; -      for (li = l->lv_first; li != NULL; li = nli) { +      for (listitem_T *li = tv_list_first(l); li != NULL;) {          if (map -            && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TV_TRANSLATE)) { +            && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, +                             TV_TRANSLATE)) {            break;          } -        nli = li->li_next;          vimvars[VV_KEY].vv_nr = idx; -        if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL -            || did_emsg) +        if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL +            || did_emsg) {            break; +        }          if (!map && rem) { -          tv_list_item_remove(l, li); +          li = tv_list_item_remove(l, li); +        } else { +          li = TV_LIST_ITEM_NEXT(l, li);          }          idx++;        } @@ -8542,8 +8578,6 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)      did_emsg |= save_did_emsg;    } - -  tv_copy(&argvars[0], rettv);  }  static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) @@ -8935,7 +8969,7 @@ static void common_function(typval_T *argvars, typval_T *rettv,            goto theend;          }          list = argvars[arg_idx].vval.v_list; -        if (list == NULL || list->lv_len == 0) { +        if (tv_list_len(list) == 0) {            arg_idx = 0;          }        } @@ -8946,7 +8980,7 @@ static void common_function(typval_T *argvars, typval_T *rettv,        // result is a VAR_PARTIAL        if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) {          const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); -        const int lv_len = (list == NULL ? 0 : list->lv_len); +        const int lv_len = tv_list_len(list);          pt->pt_argc = arg_len + lv_len;          pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * pt->pt_argc); @@ -8955,11 +8989,9 @@ static void common_function(typval_T *argvars, typval_T *rettv,            tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]);          }          if (lv_len > 0) { -          for (listitem_T *li = list->lv_first; -               li != NULL; -               li = li->li_next) { -            tv_copy(&li->li_tv, &pt->pt_argv[i++]); -          } +          TV_LIST_ITER(list, li, { +            tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); +          });          }        } @@ -9045,7 +9077,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)        li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error));        if (!error && li != NULL) { -        tv = &li->li_tv; +        tv = TV_LIST_ITEM_TV(li);        }      }    } else if (argvars[0].v_type == VAR_DICT) { @@ -9086,7 +9118,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)          }        } else if (strcmp(what, "args") == 0) {          rettv->v_type = VAR_LIST; -        if (tv_list_alloc_ret(rettv) != NULL) { +        if (tv_list_alloc_ret(rettv, pt->pt_argc) != NULL) {            for (int i = 0; i < pt->pt_argc; i++) {              tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]);            } @@ -9110,8 +9142,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)  }  /// Returns information about signs placed in a buffer as list of dicts. -static void get_buffer_signs(buf_T *buf, list_T *l) +static list_T *get_buffer_signs(buf_T *buf) +  FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT  { +  list_T *const l = tv_list_alloc(kListLenMayKnow);    for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) {      dict_T *const d = tv_dict_alloc(); @@ -9122,6 +9156,7 @@ static void get_buffer_signs(buf_T *buf, list_T *l)      tv_list_append_dict(l, d);    } +  return l;  }  /// Returns buffer options, variables and other attributes in a dictionary. @@ -9145,7 +9180,7 @@ static dict_T *get_buffer_info(buf_T *buf)    tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars);    // List of windows displaying this buffer -  list_T *const windows = tv_list_alloc(); +  list_T *const windows = tv_list_alloc(kListLenMayKnow);    FOR_ALL_TAB_WINDOWS(tp, wp) {      if (wp->w_buffer == buf) {        tv_list_append_number(windows, (varnumber_T)wp->handle); @@ -9155,9 +9190,7 @@ static dict_T *get_buffer_info(buf_T *buf)    if (buf->b_signlist != NULL) {      // List of signs placed in this buffer -    list_T *const signs = tv_list_alloc(); -    get_buffer_signs(buf, signs); -    tv_dict_add_list(dict, S_LEN("signs"), signs); +    tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf));    }    return dict; @@ -9171,7 +9204,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)    bool sel_buflisted = false;    bool sel_bufloaded = false; -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, kListLenMayKnow);    // List of all the buffers or selected buffers    if (argvars[0].v_type == VAR_DICT) { @@ -9230,35 +9263,33 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)   */  static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv)  { -  char_u      *p; - -  rettv->v_type = VAR_STRING; +  rettv->v_type = (retlist ? VAR_LIST : VAR_STRING);    rettv->vval.v_string = NULL; -  if (retlist) { -    tv_list_alloc_ret(rettv); -  } -  if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) +  if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) { +    if (retlist) { +      tv_list_alloc_ret(rettv, 0); +    }      return; +  } -  if (!retlist) { -    if (start >= 1 && start <= buf->b_ml.ml_line_count) -      p = ml_get_buf(buf, start, FALSE); -    else -      p = (char_u *)""; -    rettv->vval.v_string = vim_strsave(p); -  } else { -    if (end < start) -      return; - -    if (start < 1) +  if (retlist) { +    if (start < 1) {        start = 1; -    if (end > buf->b_ml.ml_line_count) +    } +    if (end > buf->b_ml.ml_line_count) {        end = buf->b_ml.ml_line_count; +    } +    tv_list_alloc_ret(rettv, end - start + 1);      while (start <= end) {        tv_list_append_string(rettv->vval.v_list,                              (const char *)ml_get_buf(buf, start++, false), -1);      } +  } else { +    rettv->v_type = VAR_STRING; +    rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count) +                            ? vim_strsave(ml_get_buf(buf, start, false)) +                            : NULL);    }  } @@ -9583,8 +9614,8 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)  theend:    pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); -  tv_list_alloc_ret(rettv);    ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); +  tv_list_alloc_ret(rettv, xpc.xp_numfiles);    for (int i = 0; i < xpc.xp_numfiles; i++) {      tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], @@ -9878,7 +9909,7 @@ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)    const linenr_T lnum = tv_get_lnum(argvars);    if (argvars[1].v_type == VAR_UNKNOWN) { -    end = 0; +    end = lnum;      retlist = false;    } else {      end = tv_get_lnum(&argvars[1]); @@ -9892,7 +9923,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,                              typval_T *rettv)  {    if (what_arg->v_type == VAR_UNKNOWN) { -    tv_list_alloc_ret(rettv); +    tv_list_alloc_ret(rettv, kListLenMayKnow);      if (is_qf || wp != NULL) {        (void)get_errorlist(wp, -1, rettv->vval.v_list);      } @@ -9927,7 +9958,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)    matchitem_T *cur = curwin->w_match_head;    int i; -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, kListLenMayKnow);    while (cur != NULL) {      dict_T *dict = tv_dict_alloc();      if (cur->match.regprog == NULL) { @@ -9940,7 +9971,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)          if (llpos->lnum == 0) {            break;          } -        list_T *l = tv_list_alloc(); +        list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0));          tv_list_append_number(l, (varnumber_T)llpos->lnum);          if (llpos->col > 0) {            tv_list_append_number(l, (varnumber_T)llpos->col); @@ -9989,7 +10020,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos)      fp = var2fpos(&argvars[0], true, &fnum);    } -  list_T *l = tv_list_alloc_ret(rettv); +  list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos));    tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0);    tv_list_append_number(l, ((fp != NULL)                              ? (varnumber_T)fp->lnum @@ -10062,12 +10093,12 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)    if (return_list) {      rettv->v_type = VAR_LIST; -    rettv->vval.v_list =  +    rettv->vval.v_list =        get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList);      if (rettv->vval.v_list == NULL) { -      rettv->vval.v_list = tv_list_alloc(); +      rettv->vval.v_list = tv_list_alloc(0);      } -    rettv->vval.v_list->lv_refcount++; +    tv_list_ref(rettv->vval.v_list);    } else {      rettv->v_type = VAR_STRING;      rettv->vval.v_string = get_reg_contents(regname, arg2 ? kGRegExprSrc : 0); @@ -10115,7 +10146,7 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)    tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); -  list_T *const l = tv_list_alloc(); +  list_T *const l = tv_list_alloc(kListLenMayKnow);    FOR_ALL_WINDOWS_IN_TAB(wp, tp) {      tv_list_append_number(l, (varnumber_T)wp->handle);    } @@ -10132,7 +10163,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)  {    tabpage_T *tparg = NULL; -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN +                            ? 1 +                            : kListLenMayKnow));    if (argvars[0].v_type != VAR_UNKNOWN) {      // Information about one tab page @@ -10231,7 +10264,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)  {    win_T *wparg = NULL; -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, kListLenMayKnow);    if (argvars[0].v_type != VAR_UNKNOWN) {      wparg = win_id2wp(argvars); @@ -10456,9 +10489,9 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)        rettv->vval.v_string = ExpandOne(            &xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL);      } else { -      tv_list_alloc_ret(rettv);        ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options,                  WILD_ALL_KEEP); +      tv_list_alloc_ret(rettv, xpc.xp_numfiles);        for (int i = 0; i < xpc.xp_numfiles; i++) {          tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],                                -1); @@ -10507,7 +10540,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)      if (rettv->v_type == VAR_STRING) {        rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n");      } else { -      tv_list_alloc_ret(rettv); +      tv_list_alloc_ret(rettv, ga.ga_len);        for (int i = 0; i < ga.ga_len; i++) {          tv_list_append_string(rettv->vval.v_list,                                ((const char **)(ga.ga_data))[i], -1); @@ -10646,6 +10679,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)      "windows",      "winaltkeys",      "writebackup", +#if defined(HAVE_WSL) +    "wsl", +#endif      "nvim",    }; @@ -10705,6 +10741,17 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)      n = true;    } +  if (STRICMP(name, "ruby") == 0 && n == true) { +    char *rubyhost = call_func_retstr("provider#ruby#Detect", 0, NULL, true); +    if (rubyhost) { +      if (*rubyhost == NUL) { +        // Invalid rubyhost executable. Gem is probably not installed. +        n = false; +      } +      xfree(rubyhost); +    } +  } +    rettv->vval.v_number = n;  } @@ -11024,39 +11071,42 @@ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr)   */  static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  list_T      *l; -  listitem_T  *item;    long idx = 0; -  int ic = FALSE; +  bool ic = false;    rettv->vval.v_number = -1;    if (argvars[0].v_type != VAR_LIST) {      EMSG(_(e_listreq));      return;    } -  l = argvars[0].vval.v_list; +  list_T *const l = argvars[0].vval.v_list;    if (l != NULL) { -    item = l->lv_first; +    listitem_T *item = tv_list_first(l);      if (argvars[2].v_type != VAR_UNKNOWN) {        bool error = false; -      // Start at specified item.  Use the cached index that tv_list_find() -      // sets, so that a negative number also works. -      item = tv_list_find(l, tv_get_number_chk(&argvars[2], &error)); -      idx = l->lv_idx; -      if (argvars[3].v_type != VAR_UNKNOWN) { -        ic = tv_get_number_chk(&argvars[3], &error); -      } -      if (error) { +      // Start at specified item. +      idx = tv_list_uidx(l, tv_get_number_chk(&argvars[2], &error)); +      if (error || idx == -1) {          item = NULL; +      } else { +        item = tv_list_find(l, idx); +        assert(item != NULL); +      } +      if (argvars[3].v_type != VAR_UNKNOWN) { +        ic = !!tv_get_number_chk(&argvars[3], &error); +        if (error) { +          item = NULL; +        }        }      } -    for (; item != NULL; item = item->li_next, ++idx) -      if (tv_equal(&item->li_tv, &argvars[1], ic, FALSE)) { +    for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) { +      if (tv_equal(TV_LIST_ITEM_TV(item), &argvars[1], ic, false)) {          rettv->vval.v_number = idx;          break;        } +    }    }  } @@ -11085,6 +11135,7 @@ void get_user_input(const typval_T *const argvars,    char defstr_buf[NUMBUFLEN];    char cancelreturn_buf[NUMBUFLEN];    char xp_name_buf[NUMBUFLEN]; +  char def[1] = { 0 };    if (argvars[0].v_type == VAR_DICT) {      if (argvars[1].v_type != VAR_UNKNOWN) {        emsgf(_("E5050: {opts} must be the only argument")); @@ -11099,7 +11150,6 @@ void get_user_input(const typval_T *const argvars,      if (defstr == NULL) {        return;      } -    char def[1] = { 0 };      cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"),                                                cancelreturn_buf, def);      if (cancelreturn == NULL) {  // error @@ -11220,11 +11270,10 @@ static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr)   */  static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  listitem_T  *li;    int selected;    int mouse_used; -  if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) { +  if (argvars[0].v_type != VAR_LIST) {      EMSG2(_(e_listarg), "inputlist()");      return;    } @@ -11235,15 +11284,16 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)    msg_scroll = TRUE;    msg_clr_eos(); -  for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) { -    msg_puts(tv_get_string(&li->li_tv)); +  TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { +    msg_puts(tv_get_string(TV_LIST_ITEM_TV(li)));      msg_putchar('\n'); -  } +  }); -  /* Ask for choice. */ +  // Ask for choice.    selected = prompt_for_number(&mouse_used); -  if (mouse_used) +  if (mouse_used) {      selected -= lines_left; +  }    rettv->vval.v_number = selected;  } @@ -11299,9 +11349,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)    if (argvars[0].v_type != VAR_LIST) {      EMSG2(_(e_listarg), "insert()"); -  } else if ((l = argvars[0].vval.v_list) != NULL -             && !tv_check_lock(l->lv_lock, N_("insert() argument"), -                               TV_TRANSLATE)) { +  } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), +                            N_("insert() argument"), TV_TRANSLATE)) {      long before = 0;      if (argvars[2].v_type != VAR_UNKNOWN) {        before = tv_get_number_chk(&argvars[2], &error); @@ -11312,7 +11361,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)      }      listitem_T *item = NULL; -    if (before != l->lv_len) { +    if (before != tv_list_len(l)) {        item = tv_list_find(l, before);        if (item == NULL) {          EMSGN(_(e_listidx), before); @@ -11376,7 +11425,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)          EMSG2(_(e_dictkey), lv.ll_newkey);        } else if (lv.ll_list != NULL) {          // List item. -        rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv); +        rettv->vval.v_number = tv_islocked(TV_LIST_ITEM_TV(lv.ll_li));        } else {          // Dictionary item.          rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv); @@ -11405,43 +11454,41 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,      return;    } -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict));    TV_DICT_ITER(tv->vval.v_dict, di, { -    listitem_T *const li = tv_list_item_alloc(); -    tv_list_append(rettv->vval.v_list, li); +    typval_T tv = { .v_lock = VAR_UNLOCKED };      switch (what) {        case kDictListKeys: { -        li->li_tv.v_type = VAR_STRING; -        li->li_tv.v_lock = VAR_UNLOCKED; -        li->li_tv.vval.v_string = vim_strsave(di->di_key); +        tv.v_type = VAR_STRING; +        tv.vval.v_string = vim_strsave(di->di_key);          break;        }        case kDictListValues: { -        tv_copy(&di->di_tv, &li->li_tv); +        tv_copy(&di->di_tv, &tv);          break;        }        case kDictListItems: {          // items() -        list_T *const sub_l = tv_list_alloc(); -        li->li_tv.v_type = VAR_LIST; -        li->li_tv.v_lock = VAR_UNLOCKED; -        li->li_tv.vval.v_list = sub_l; -        sub_l->lv_refcount++; - -        listitem_T *sub_li = tv_list_item_alloc(); -        tv_list_append(sub_l, sub_li); -        sub_li->li_tv.v_type = VAR_STRING; -        sub_li->li_tv.v_lock = VAR_UNLOCKED; -        sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); - -        sub_li = tv_list_item_alloc(); -        tv_list_append(sub_l, sub_li); -        tv_copy(&di->di_tv, &sub_li->li_tv); +        list_T *const sub_l = tv_list_alloc(2); +        tv.v_type = VAR_LIST; +        tv.vval.v_list = sub_l; +        tv_list_ref(sub_l); + +        tv_list_append_owned_tv(sub_l, (typval_T) { +          .v_type = VAR_STRING, +          .v_lock = VAR_UNLOCKED, +          .vval.v_string = (char_u *)xstrdup((const char *)di->di_key), +        }); + +        tv_list_append_tv(sub_l, &di->di_tv); +          break;        }      } + +    tv_list_append_owned_tv(rettv->vval.v_list, tv);    });  } @@ -11536,15 +11583,13 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable)    }    list_T *argl = cmd_tv->vval.v_list; -  int argc = argl->lv_len; +  int argc = tv_list_len(argl);    if (!argc) {      EMSG(_(e_invarg));  // List must have at least one item.      return NULL;    } -  assert(argl->lv_first); - -  const char *exe = tv_get_string_chk(&argl->lv_first->li_tv); +  const char *exe = tv_get_string_chk(TV_LIST_ITEM_TV(tv_list_first(argl)));    if (!exe || !os_can_exe((const char_u *)exe, NULL, true)) {      if (exe && executable) {        *executable = false; @@ -11559,15 +11604,15 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable)    // Build the argument vector    int i = 0;    char **argv = xcalloc(argc + 1, sizeof(char *)); -  for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { -    const char *a = tv_get_string_chk(&arg->li_tv); +  TV_LIST_ITER_CONST(argl, arg, { +    const char *a = tv_get_string_chk(TV_LIST_ITEM_TV(arg));      if (!a) {        // Did emsg in tv_get_string_chk; just deallocate argv.        shell_free_argv(argv);        return NULL;      }      argv[i++] = xstrdup(a); -  } +  });    return argv;  } @@ -11695,7 +11740,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)    list_T *args = argvars[0].vval.v_list; -  Channel **jobs = xcalloc(args->lv_len, sizeof(*jobs)); +  Channel **jobs = xcalloc(tv_list_len(args), sizeof(*jobs));    ui_busy_start();    MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); @@ -11704,10 +11749,10 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)    // -1 for jobs that were skipped or timed out.    int i = 0; -  for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next, i++) { +  TV_LIST_ITER_CONST(args, arg, {      Channel *chan = NULL; -    if (arg->li_tv.v_type != VAR_NUMBER -        || !(chan = find_job(arg->li_tv.vval.v_number, false))) { +    if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER +        || !(chan = find_job(TV_LIST_ITEM_TV(arg)->vval.v_number, false))) {        jobs[i] = NULL;      } else {        jobs[i] = chan; @@ -11719,7 +11764,8 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)          multiqueue_replace_parent(chan->events, waiting_jobs);        }      } -  } +    i++; +  });    int remaining = -1;    uint64_t before = 0; @@ -11728,7 +11774,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)      before = os_hrtime();    } -  for (i = 0; i < args->lv_len; i++) { +  for (i = 0; i < tv_list_len(args); i++) {      if (remaining == 0) {        // timed out        break; @@ -11755,10 +11801,10 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)      }    } -  list_T *rv = tv_list_alloc(); +  list_T *const rv = tv_list_alloc(tv_list_len(args));    // restore the parent queue for any jobs still alive -  for (i = 0; i < args->lv_len; i++) { +  for (i = 0; i < tv_list_len(args); i++) {      if (jobs[i] == NULL) {        tv_list_append_number(rv, -3);        continue; @@ -11774,7 +11820,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)    multiqueue_free(waiting_jobs);    xfree(jobs);    ui_busy_stop(); -  rv->lv_refcount++; +  tv_list_ref(rv);    rettv->v_type = VAR_LIST;    rettv->vval.v_list = rv;  } @@ -11788,9 +11834,6 @@ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)      EMSG(_(e_listreq));      return;    } -  if (argvars[0].vval.v_list == NULL) { -    return; -  }    const char *const sep = (argvars[1].v_type == VAR_UNKNOWN                             ? " "                             : tv_get_string_chk(&argvars[1])); @@ -12161,7 +12204,8 @@ static void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr)  } -static void find_some_match(typval_T *argvars, typval_T *rettv, int type) +static void find_some_match(typval_T *const argvars, typval_T *const rettv, +                            const SomeMatchType type)  {    char_u      *str = NULL;    long        len = 0; @@ -12182,25 +12226,38 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)    p_cpo = (char_u *)"";    rettv->vval.v_number = -1; -  if (type == 3 || type == 4) { -    // type 3: return empty list when there are no matches. -    // type 4: return ["", -1, -1, -1] -    tv_list_alloc_ret(rettv); -    if (type == 4) { +  switch (type) { +    // matchlist(): return empty list when there are no matches. +    case kSomeMatchList: { +      tv_list_alloc_ret(rettv, kListLenMayKnow); +      break; +    } +    // matchstrpos(): return ["", -1, -1, -1] +    case kSomeMatchStrPos: { +      tv_list_alloc_ret(rettv, 4);        tv_list_append_string(rettv->vval.v_list, "", 0);        tv_list_append_number(rettv->vval.v_list, -1);        tv_list_append_number(rettv->vval.v_list, -1);        tv_list_append_number(rettv->vval.v_list, -1); +      break; +    } +    case kSomeMatchStr: { +      rettv->v_type = VAR_STRING; +      rettv->vval.v_string = NULL; +      break; +    } +    case kSomeMatch: +    case kSomeMatchEnd: { +      // Do nothing: zero is default. +      break;      } -  } else if (type == 2) { -    rettv->v_type = VAR_STRING; -    rettv->vval.v_string = NULL;    }    if (argvars[0].v_type == VAR_LIST) { -    if ((l = argvars[0].vval.v_list) == NULL) +    if ((l = argvars[0].vval.v_list) == NULL) {        goto theend; -    li = l->lv_first; +    } +    li = tv_list_first(l);    } else {      expr = str = (char_u *)tv_get_string(&argvars[0]);      len = (long)STRLEN(str); @@ -12220,11 +12277,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)        goto theend;      }      if (l != NULL) { -      li = tv_list_find(l, start); -      if (li == NULL) { +      idx = tv_list_uidx(l, start); +      if (idx == -1) {          goto theend;        } -      idx = l->lv_idx;  // Use the cached index. +      li = tv_list_find(l, idx);      } else {        if (start < 0)          start = 0; @@ -12260,7 +12317,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)            break;          }          xfree(tofree); -        tofree = expr = str = (char_u *)encode_tv2echo(&li->li_tv, NULL); +        tofree = expr = str = (char_u *)encode_tv2echo(TV_LIST_ITEM_TV(li), +                                                       NULL);          if (str == NULL) {            break;          } @@ -12275,8 +12333,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)        /* Advance to just after the match. */        if (l != NULL) { -        li = li->li_next; -        ++idx; +        li = TV_LIST_ITEM_NEXT(l, li); +        idx++;        } else {          startcol = (colnr_T)(regmatch.startp[0]                               + (*mb_ptr2len)(regmatch.startp[0]) - str); @@ -12288,62 +12346,76 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)      }      if (match) { -      if (type == 4) { -        listitem_T *li1 = rettv->vval.v_list->lv_first; -        listitem_T *li2 = li1->li_next; -        listitem_T *li3 = li2->li_next; -        listitem_T *li4 = li3->li_next; -        xfree(li1->li_tv.vval.v_string); - -        int rd = (int)(regmatch.endp[0] - regmatch.startp[0]); -        li1->li_tv.vval.v_string = vim_strnsave(regmatch.startp[0], rd); -        li3->li_tv.vval.v_number = (varnumber_T)(regmatch.startp[0] - expr); -        li4->li_tv.vval.v_number = (varnumber_T)(regmatch.endp[0] - expr); -        if (l != NULL) { -          li2->li_tv.vval.v_number = (varnumber_T)idx; +      switch (type) { +        case kSomeMatchStrPos: { +          list_T *const ret_l = rettv->vval.v_list; +          listitem_T *li1 = tv_list_first(ret_l); +          listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1); +          listitem_T *li3 = TV_LIST_ITEM_NEXT(ret_l, li2); +          listitem_T *li4 = TV_LIST_ITEM_NEXT(ret_l, li3); +          xfree(TV_LIST_ITEM_TV(li1)->vval.v_string); + +          const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]); +          TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz( +              (const char *)regmatch.startp[0], rd); +          TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)( +              regmatch.startp[0] - expr); +          TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)( +              regmatch.endp[0] - expr); +          if (l != NULL) { +            TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; +          } +          break;          } -      } else if (type == 3) { -        int i; - -        /* return list with matched string and submatches */ -        for (i = 0; i < NSUBEXP; ++i) { -          if (regmatch.endp[i] == NULL) { -            tv_list_append_string(rettv->vval.v_list, NULL, 0); -          } else { -            tv_list_append_string(rettv->vval.v_list, -                                  (const char *)regmatch.startp[i], -                                  (regmatch.endp[i] - regmatch.startp[i])); +        case kSomeMatchList: { +          // Return list with matched string and submatches. +          for (int i = 0; i < NSUBEXP; i++) { +            if (regmatch.endp[i] == NULL) { +              tv_list_append_string(rettv->vval.v_list, NULL, 0); +            } else { +              tv_list_append_string(rettv->vval.v_list, +                                    (const char *)regmatch.startp[i], +                                    (regmatch.endp[i] - regmatch.startp[i])); +            }            } +          break;          } -      } else if (type == 2) { -        // Return matched string. -        if (l != NULL) { -          tv_copy(&li->li_tv, rettv); -        } else { -          rettv->vval.v_string = (char_u *)xmemdupz( -              (const char *)regmatch.startp[0], -              (size_t)(regmatch.endp[0] - regmatch.startp[0])); +        case kSomeMatchStr: { +          // Return matched string. +          if (l != NULL) { +            tv_copy(TV_LIST_ITEM_TV(li), rettv); +          } else { +            rettv->vval.v_string = (char_u *)xmemdupz( +                (const char *)regmatch.startp[0], +                (size_t)(regmatch.endp[0] - regmatch.startp[0])); +          } +          break;          } -      } else if (l != NULL) { -        rettv->vval.v_number = idx; -      } else { -        if (type != 0) { -          rettv->vval.v_number = -            (varnumber_T)(regmatch.startp[0] - str); -        } else { -          rettv->vval.v_number = -            (varnumber_T)(regmatch.endp[0] - str); +        case kSomeMatch: +        case kSomeMatchEnd: { +          if (l != NULL) { +            rettv->vval.v_number = idx; +          } else { +            if (type == kSomeMatch) { +              rettv->vval.v_number = +                (varnumber_T)(regmatch.startp[0] - str); +            } else { +              rettv->vval.v_number = +                (varnumber_T)(regmatch.endp[0] - str); +            } +            rettv->vval.v_number += (varnumber_T)(str - expr); +          } +          break;          } -        rettv->vval.v_number += (varnumber_T)(str - expr);        }      }      vim_regfree(regmatch.regprog);    } -  if (type == 4 && l == NULL) { +  if (type == kSomeMatchStrPos && l == NULL) {      // matchstrpos() without a list: drop the second item -    tv_list_item_remove(rettv->vval.v_list, -                        rettv->vval.v_list->lv_first->li_next); +    list_T *const ret_l = rettv->vval.v_list; +    tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l)));    }  theend: @@ -12356,7 +12428,7 @@ theend:   */  static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  find_some_match(argvars, rettv, 1); +  find_some_match(argvars, rettv, kSomeMatch);  }  /* @@ -12409,56 +12481,56 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)  static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -    rettv->vval.v_number = -1; +  rettv->vval.v_number = -1; -    char buf[NUMBUFLEN]; -    const char *const group = tv_get_string_buf_chk(&argvars[0], buf); -    if (group == NULL) { -        return; -    } +  char buf[NUMBUFLEN]; +  const char *const group = tv_get_string_buf_chk(&argvars[0], buf); +  if (group == NULL) { +    return; +  } -    if (argvars[1].v_type != VAR_LIST) { -        EMSG2(_(e_listarg), "matchaddpos()"); -        return; -    } +  if (argvars[1].v_type != VAR_LIST) { +    EMSG2(_(e_listarg), "matchaddpos()"); +    return; +  } -    list_T *l; -    l = argvars[1].vval.v_list; -    if (l == NULL) { -        return; -    } +  list_T *l; +  l = argvars[1].vval.v_list; +  if (l == NULL) { +    return; +  } -    bool error = false; -    int prio = 10; -    int id = -1; -    const char *conceal_char = NULL; +  bool error = false; +  int prio = 10; +  int id = -1; +  const char *conceal_char = NULL; -    if (argvars[2].v_type != VAR_UNKNOWN) { -      prio = tv_get_number_chk(&argvars[2], &error); -      if (argvars[3].v_type != VAR_UNKNOWN) { -        id = tv_get_number_chk(&argvars[3], &error); -        if (argvars[4].v_type != VAR_UNKNOWN) { -          if (argvars[4].v_type != VAR_DICT) { -            EMSG(_(e_dictreq)); -            return; -          } -          dictitem_T *di; -          if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) -              != NULL) { -            conceal_char = tv_get_string(&di->di_tv); -          } +  if (argvars[2].v_type != VAR_UNKNOWN) { +    prio = tv_get_number_chk(&argvars[2], &error); +    if (argvars[3].v_type != VAR_UNKNOWN) { +      id = tv_get_number_chk(&argvars[3], &error); +      if (argvars[4].v_type != VAR_UNKNOWN) { +        if (argvars[4].v_type != VAR_DICT) { +          EMSG(_(e_dictreq)); +          return; +        } +        dictitem_T *di; +        if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) +            != NULL) { +          conceal_char = tv_get_string(&di->di_tv);          }        }      } -    if (error == true) { -        return; -    } +  } +  if (error == true) { +    return; +  } -    // id == 3 is ok because matchaddpos() is supposed to substitute :3match  -    if (id == 1 || id == 2) { -        EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); -        return; -    } +  // id == 3 is ok because matchaddpos() is supposed to substitute :3match +  if (id == 1 || id == 2) { +    EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); +    return; +  }    rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l,                                     conceal_char); @@ -12469,14 +12541,16 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)   */  static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  tv_list_alloc_ret(rettv); +  const int id = tv_get_number(&argvars[0]); -  int id = tv_get_number(&argvars[0]); +  tv_list_alloc_ret(rettv, (id >= 1 && id <= 3 +                            ? 2 +                            : 0));    if (id >= 1 && id <= 3) { -    matchitem_T *m; +    matchitem_T *const m = (matchitem_T *)get_match(curwin, id); -    if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) { +    if (m != NULL) {        tv_list_append_string(rettv->vval.v_list,                              (const char *)syn_id2name(m->hlg_id), -1);        tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); @@ -12501,7 +12575,7 @@ static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr)   */  static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  find_some_match(argvars, rettv, 0); +  find_some_match(argvars, rettv, kSomeMatchEnd);  }  /* @@ -12509,7 +12583,7 @@ static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr)   */  static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  find_some_match(argvars, rettv, 3); +  find_some_match(argvars, rettv, kSomeMatchList);  }  /* @@ -12517,13 +12591,13 @@ static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)   */  static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  find_some_match(argvars, rettv, 2); +  find_some_match(argvars, rettv, kSomeMatchStr);  }  /// "matchstrpos()" function  static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  find_some_match(argvars, rettv, 4); +  find_some_match(argvars, rettv, kSomeMatchStrPos);  }  /// Get maximal/minimal number value in a list or dictionary @@ -12539,41 +12613,41 @@ static void max_min(const typval_T *const tv, typval_T *const rettv,                      const bool domax)    FUNC_ATTR_NONNULL_ALL  { -  varnumber_T n = 0;    bool error = false; +  rettv->vval.v_number = 0; +  varnumber_T n = (domax ? VARNUMBER_MIN : VARNUMBER_MAX);    if (tv->v_type == VAR_LIST) { -    const list_T *const l = tv->vval.v_list; -    if (tv_list_len(l) != 0) { -      n = tv_get_number_chk(&l->lv_first->li_tv, &error); -      for (const listitem_T *li = l->lv_first->li_next; li != NULL && !error; -           li = li->li_next) { -        const varnumber_T i = tv_get_number_chk(&li->li_tv, &error); -        if (domax ? i > n : i < n) { -          n = i; -        } -      } +    if (tv_list_len(tv->vval.v_list) == 0) { +      return;      } +    TV_LIST_ITER_CONST(tv->vval.v_list, li, { +      const varnumber_T i = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); +      if (error) { +        return; +      } +      if (domax ? i > n : i < n) { +        n = i; +      } +    });    } else if (tv->v_type == VAR_DICT) { -    if (tv->vval.v_dict != NULL) { -      bool first = true; -      TV_DICT_ITER(tv->vval.v_dict, di, { -        const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); -        if (error) { -          break; -        } -        if (first) { -          n = i; -          first = true; -        } else if (domax ? i > n : i < n) { -          n = i; -        } -      }); +    if (tv_dict_len(tv->vval.v_dict) == 0) { +      return;      } +    TV_DICT_ITER(tv->vval.v_dict, di, { +      const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); +      if (error) { +        return; +      } +      if (domax ? i > n : i < n) { +        n = i; +      } +    });    } else {      EMSG2(_(e_listdictarg), domax ? "max()" : "min()"); +    return;    } -  rettv->vval.v_number = error ? 0 : n; +  rettv->vval.v_number = n;  }  /* @@ -12658,23 +12732,20 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr)      EMSG2(_(e_listarg), "msgpackdump()");      return;    } -  list_T *ret_list = tv_list_alloc_ret(rettv); -  const list_T *list = argvars[0].vval.v_list; -  if (list == NULL) { -    return; -  } +  list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); +  list_T *const list = argvars[0].vval.v_list;    msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write);    const char *const msg = _("msgpackdump() argument, index %i");    // Assume that translation will not take more then 4 times more space    char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN];    int idx = 0; -  for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { -    vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx); +  TV_LIST_ITER(list, li, { +    vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx);      idx++; -    if (encode_vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) { +    if (encode_vim_to_msgpack(lpacker, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) {        break;      } -  } +  });    msgpack_packer_free(lpacker);  } @@ -12686,12 +12757,12 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)      EMSG2(_(e_listarg), "msgpackparse()");      return;    } -  list_T *ret_list = tv_list_alloc_ret(rettv); -  const list_T *list = argvars[0].vval.v_list; -  if (list == NULL || list->lv_first == NULL) { +  list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); +  const list_T *const list = argvars[0].vval.v_list; +  if (tv_list_len(list) == 0) {      return;    } -  if (list->lv_first->li_tv.v_type != VAR_STRING) { +  if (TV_LIST_ITEM_TV(tv_list_first(list))->v_type != VAR_STRING) {      EMSG2(_(e_invarg2), "List item is not a string");      return;    } @@ -12731,13 +12802,12 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)          goto f_msgpackparse_exit;        }        if (result == MSGPACK_UNPACK_SUCCESS) { -        listitem_T *li = tv_list_item_alloc(); -        li->li_tv.v_type = VAR_UNKNOWN; -        tv_list_append(ret_list, li); -        if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { +        typval_T tv = { .v_type = VAR_UNKNOWN }; +        if (msgpack_to_vim(unpacked.data, &tv) == FAIL) {            EMSG2(_(e_invarg2), "Failed to convert msgpack string");            goto f_msgpackparse_exit;          } +        tv_list_append_owned_tv(ret_list, tv);        }        if (result == MSGPACK_UNPACK_CONTINUE) {          if (rlret == OK) { @@ -12943,7 +13013,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)    } else if (stride > 0 ? end + 1 < start : end - 1 > start) {      emsgf(_("E727: Start past end"));    } else { -    tv_list_alloc_ret(rettv); +    tv_list_alloc_ret(rettv, (end - start) / stride);      for (i = start; stride > 0 ? i <= end : i >= end; i += stride) {        tv_list_append_number(rettv->vval.v_list, (varnumber_T)i);      } @@ -12964,9 +13034,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)    long prevlen  = 0;                    /* length of data in prev */    long prevsize = 0;                    /* size of prev buffer */    long maxline  = MAXLNUM; -  long cnt      = 0; -  char_u      *p;                       /* position in buf */ -  char_u      *start;                   /* start of current line */    if (argvars[1].v_type != VAR_UNKNOWN) {      if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { @@ -12977,7 +13044,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)      }    } -  tv_list_alloc_ret(rettv); +  list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown);    // Always open the file in binary mode, library functions have a mind of    // their own about CR-LF conversion. @@ -12987,19 +13054,20 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)      return;    } -  while (cnt < maxline || maxline < 0) { +  while (maxline < 0 || tv_list_len(l) < maxline) {      readlen = (int)fread(buf, 1, io_size, fd); -    /* This for loop processes what was read, but is also entered at end -     * of file so that either: -     * - an incomplete line gets written -     * - a "binary" file gets an empty line at the end if it ends in a -     *   newline.  */ +    // This for loop processes what was read, but is also entered at end +    // of file so that either: +    // - an incomplete line gets written +    // - a "binary" file gets an empty line at the end if it ends in a +    //   newline. +    char_u *p;  // Position in buf. +    char_u *start;  // Start of current line.      for (p = buf, start = buf;           p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); -         ++p) { +         p++) {        if (*p == '\n' || readlen <= 0) { -        listitem_T  *li;          char_u      *s  = NULL;          size_t len = p - start; @@ -13026,22 +13094,32 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)            prevlen = prevsize = 0;          } -        li = tv_list_item_alloc(); -        li->li_tv.v_type = VAR_STRING; -        li->li_tv.v_lock = 0; -        li->li_tv.vval.v_string = s; -        tv_list_append(rettv->vval.v_list, li); - -        start = p + 1;         /* step over newline */ -        if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) +        tv_list_append_owned_tv(l, (typval_T) { +          .v_type = VAR_STRING, +          .v_lock = VAR_UNLOCKED, +          .vval.v_string = s, +        }); + +        start = p + 1;  // Step over newline. +        if (maxline < 0) { +          if (tv_list_len(l) > -maxline) { +            assert(tv_list_len(l) == 1 + (-maxline)); +            tv_list_item_remove(l, tv_list_first(l)); +          } +        } else if (tv_list_len(l) >= maxline) { +          assert(tv_list_len(l) == maxline); +          break; +        } +        if (readlen <= 0) {            break; -      } else if (*p == NUL) +        } +      } else if (*p == NUL) {          *p = '\n'; -      /* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF.  Do this -       * when finding the BF and check the previous two bytes. */ -      else if (*p == 0xbf && enc_utf8 && !binary) { -        /* Find the two bytes before the 0xbf.	If p is at buf, or buf -         * + 1, these may be in the "prev" string. */ +      // Check for utf8 "bom"; U+FEFF is encoded as EF BB BF.  Do this +      // when finding the BF and check the previous two bytes. +      } else if (*p == 0xbf && !binary) { +        // Find the two bytes before the 0xbf.  If p is at buf, or buf + 1, +        // these may be in the "prev" string.          char_u back1 = p >= buf + 1 ? p[-1]                         : prevlen >= 1 ? prev[prevlen - 1] : NUL;          char_u back2 = p >= buf + 2 ? p[-2] @@ -13075,8 +13153,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)        }      }     /* for */ -    if ((cnt >= maxline && maxline >= 0) || readlen <= 0) +    if ((maxline >= 0 && tv_list_len(l) >= maxline) || readlen <= 0) {        break; +    }      if (start < p) {        /* There's part of a line in buf, store it in "prev". */        if (p - start + prevlen >= prevsize) { @@ -13100,16 +13179,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)      }    }   /* while */ -  /* -   * For a negative line count use only the lines at the end of the file, -   * free the rest. -   */ -  if (maxline < 0) -    while (cnt > -maxline) { -      tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); -      cnt--; -    } -    xfree(prev);    fclose(fd);  } @@ -13123,9 +13192,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)  /// @return OK In case of success, FAIL in case of error  static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL  { -  if (arg->v_type != VAR_LIST -      || arg->vval.v_list == NULL -      || arg->vval.v_list->lv_len != 2) { +  if (arg->v_type != VAR_LIST || tv_list_len(arg->vval.v_list) != 2) {      return FAIL;    } @@ -13190,7 +13257,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)    STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u),        "type punning will produce incorrect results on this platform"); -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, 2);    tv_list_append_number(rettv->vval.v_list, u.split.high);    tv_list_append_number(rettv->vval.v_list, u.split.low);  } @@ -13249,8 +13316,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)      }    } else if (argvars[0].v_type != VAR_LIST) {      EMSG2(_(e_listdictarg), "remove()"); -  } else if ((l = argvars[0].vval.v_list) != NULL -             && !tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) { +  } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), +                            arg_errmsg, TV_TRANSLATE)) {      bool error = false;      idx = tv_get_number_chk(&argvars[1], &error); @@ -13261,8 +13328,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)      } else {        if (argvars[2].v_type == VAR_UNKNOWN) {          // Remove one item, return its value. -        tv_list_remove_items(l, item, item); -        *rettv = item->li_tv; +        tv_list_drop_items(l, item, item); +        *rettv = *TV_LIST_ITEM_TV(item);          xfree(item);        } else {          // Remove range of items, return list with values. @@ -13274,21 +13341,17 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)          } else {            int cnt = 0; -          for (li = item; li != NULL; li = li->li_next) { -            ++cnt; -            if (li == item2) +          for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { +            cnt++; +            if (li == item2) {                break; +            }            }            if (li == NULL) {  // Didn't find "item2" after "item".              emsgf(_(e_invrange));            } else { -            tv_list_remove_items(l, item, item2); -            l = tv_list_alloc_ret(rettv); -            l->lv_first = item; -            l->lv_last = item2; -            item->li_prev = NULL; -            item2->li_next = NULL; -            l->lv_len = cnt; +            tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt), +                               cnt);            }          }        } @@ -13318,7 +13381,7 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)  {    varnumber_T n = tv_get_number(&argvars[1]);    if (argvars[0].v_type == VAR_LIST) { -    tv_list_alloc_ret(rettv); +    tv_list_alloc_ret(rettv, (n > 0) * n * tv_list_len(argvars[0].vval.v_list));      while (n-- > 0) {        tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL);      } @@ -13433,7 +13496,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)            q[-1] = NUL;            q = (char *)path_tail((char_u *)p);          } -        if (q > p && !path_is_absolute_path((const char_u *)buf)) { +        if (q > p && !path_is_absolute((const char_u *)buf)) {            // Symlink is relative to directory of argument. Replace the            // symlink with the resolved name in the same directory.            const size_t p_len = strlen(p); @@ -13524,21 +13587,12 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)    list_T *l;    if (argvars[0].v_type != VAR_LIST) {      EMSG2(_(e_listarg), "reverse()"); -  } else if ((l = argvars[0].vval.v_list) != NULL -             && !tv_check_lock(l->lv_lock, N_("reverse() argument"), -                               TV_TRANSLATE)) { -    listitem_T *li = l->lv_last; -    l->lv_first = l->lv_last = NULL; -    l->lv_len = 0; -    while (li != NULL) { -      listitem_T *const ni = li->li_prev; -      tv_list_append(l, li); -      li = ni; -    } +  } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), +                            N_("reverse() argument"), TV_TRANSLATE)) { +    tv_list_reverse(l);      rettv->vval.v_list = l;      rettv->v_type = VAR_LIST; -    l->lv_refcount++; -    l->lv_idx = l->lv_len - l->lv_idx - 1; +    tv_list_ref(l);    }  } @@ -13760,7 +13814,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)    scid_T save_current_SID;    uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match;    linenr_T save_sourcing_lnum; -  int save_autocmd_fname_full, save_autocmd_bufnr; +  int save_autocmd_bufnr;    void *save_funccalp;    if (l_provider_call_nesting) { @@ -13771,16 +13825,14 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)      save_sourcing_lnum = sourcing_lnum;      save_autocmd_fname = autocmd_fname;      save_autocmd_match = autocmd_match; -    save_autocmd_fname_full = autocmd_fname_full;      save_autocmd_bufnr = autocmd_bufnr;      save_funccalp = save_funccal(); -    // +      current_SID = provider_caller_scope.SID;      sourcing_name = provider_caller_scope.sourcing_name;      sourcing_lnum = provider_caller_scope.sourcing_lnum;      autocmd_fname = provider_caller_scope.autocmd_fname;      autocmd_match = provider_caller_scope.autocmd_match; -    autocmd_fname_full = provider_caller_scope.autocmd_fname_full;      autocmd_bufnr = provider_caller_scope.autocmd_bufnr;      restore_funccal(provider_caller_scope.funccalp);    } @@ -13796,7 +13848,6 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)      sourcing_lnum = save_sourcing_lnum;      autocmd_fname = save_autocmd_fname;      autocmd_match = save_autocmd_match; -    autocmd_fname_full = save_autocmd_fname_full;      autocmd_bufnr = save_autocmd_bufnr;      restore_funccal(save_funccalp);    } @@ -13836,14 +13887,17 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)    int argsl = 0;    if (argvars[1].v_type == VAR_LIST) {      args = argvars[1].vval.v_list; -    argsl = args->lv_len; +    argsl = tv_list_len(args);      // Assert that all list items are strings -    for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { -      if (arg->li_tv.v_type != VAR_STRING) { -        EMSG(_(e_invarg)); +    int i = 0; +    TV_LIST_ITER_CONST(args, arg, { +      if (TV_LIST_ITEM_TV(arg)->v_type != VAR_STRING) { +        emsgf(_("E5010: List item %d of the second argument is not a string"), +              i);          return;        } -    } +      i++; +    });    }    if (argvars[0].vval.v_string == NULL || argvars[0].vval.v_string[0] == NUL) { @@ -13861,9 +13915,9 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)    int i = 1;    // Copy arguments to the vector    if (argsl > 0) { -    for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { -      argv[i++] = xstrdup(tv_get_string(&arg->li_tv)); -    } +    TV_LIST_ITER_CONST(args, arg, { +      argv[i++] = xstrdup(tv_get_string(TV_LIST_ITEM_TV(arg))); +    });    }    // The last item of argv must be NULL @@ -14094,7 +14148,7 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)    int lnum = 0;    int col = 0; -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, 2);    if (searchpair_cmn(argvars, &match_pos) > 0) {      lnum = match_pos.lnum; @@ -14262,18 +14316,14 @@ do_searchpair (  static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)  {    pos_T match_pos; -  int lnum = 0; -  int col = 0; -  int n;    int flags = 0; -  tv_list_alloc_ret(rettv); +  const int n = search_cmn(argvars, &match_pos, &flags); -  n = search_cmn(argvars, &match_pos, &flags); -  if (n > 0) { -    lnum = match_pos.lnum; -    col = match_pos.col; -  } +  tv_list_alloc_ret(rettv, 2 + (!!(flags & SP_SUBPAT))); + +  const int lnum = (n > 0 ? match_pos.lnum : 0); +  const int col = (n > 0 ? match_pos.col : 0);    tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum);    tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); @@ -14289,13 +14339,9 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)    char **addrs = server_address_list(&n);    // Copy addrs into a linked list. -  list_T *l = tv_list_alloc_ret(rettv); +  list_T *const l = tv_list_alloc_ret(rettv, n);    for (size_t i = 0; i < n; i++) { -    listitem_T *li = tv_list_item_alloc(); -    li->li_tv.v_type = VAR_STRING; -    li->li_tv.v_lock = 0; -    li->li_tv.vval.v_string = (char_u *)addrs[i]; -    tv_list_append(l, li); +    tv_list_append_allocated_string(l, addrs[i]);    }    xfree(addrs);  } @@ -14357,8 +14403,11 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)      return;    } +  rettv->v_type = VAR_NUMBER; +  rettv->vval.v_number = 0;    if (argvars[0].vval.v_string) { -    server_stop((char *) argvars[0].vval.v_string); +    bool rv = server_stop((char *)argvars[0].vval.v_string); +    rettv->vval.v_number = (rv ? 1 : 0);    }  } @@ -14503,25 +14552,26 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)    const char *line = NULL;    if (argvars[1].v_type == VAR_LIST) {      l = argvars[1].vval.v_list; -    li = l->lv_first; +    li = tv_list_first(l);    } else {      line = tv_get_string_chk(&argvars[1]);    } -  /* default result is zero == OK */ +  // Default result is zero == OK.    for (;; ) { -    if (l != NULL) { +    if (argvars[1].v_type == VAR_LIST) {        // List argument, get next string.        if (li == NULL) {          break;        } -      line = tv_get_string_chk(&li->li_tv); -      li = li->li_next; +      line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); +      li = TV_LIST_ITEM_NEXT(l, li);      } -    rettv->vval.v_number = 1;           /* FAIL */ -    if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) +    rettv->vval.v_number = 1;  // FAIL +    if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) {        break; +    }      /* When coming here from Insert mode, sync undo, so that this can be       * undone separately from what was previously inserted. */ @@ -14593,7 +14643,8 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)      return;    }    const char *const act = tv_get_string_chk(action_arg); -  if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) { +  if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f') +      && act[1] == NUL) {      action = *act;    } else {      EMSG2(_(e_invact), act); @@ -14622,8 +14673,8 @@ skip_args:      title = (wp ? "setloclist()" : "setqflist()");    } -  list_T *l = list_arg->vval.v_list; -  if (l && set_errorlist(wp, l, action, (char_u *)title, d) == OK) { +  list_T *const l = list_arg->vval.v_list; +  if (set_errorlist(wp, l, action, (char_u *)title, d) == OK) {      rettv->vval.v_number = 0;    }  } @@ -14648,8 +14699,6 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)   */  static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  list_T      *l; -  listitem_T  *li;    dict_T      *d;    list_T      *s = NULL; @@ -14658,92 +14707,89 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)      EMSG(_(e_listreq));      return;    } -  if ((l = argvars[0].vval.v_list) != NULL) { - -    /* To some extent make sure that we are dealing with a list from -     * "getmatches()". */ -    li = l->lv_first; -    while (li != NULL) { -      if (li->li_tv.v_type != VAR_DICT -          || (d = li->li_tv.vval.v_dict) == NULL) { -        EMSG(_(e_invarg)); -        return; -      } -      if (!(tv_dict_find(d, S_LEN("group")) != NULL -            && (tv_dict_find(d, S_LEN("pattern")) != NULL -                || tv_dict_find(d, S_LEN("pos1")) != NULL) -            && tv_dict_find(d, S_LEN("priority")) != NULL -            && tv_dict_find(d, S_LEN("id")) != NULL)) { -        EMSG(_(e_invarg)); -        return; -      } -      li = li->li_next; +  list_T *const l = argvars[0].vval.v_list; +  // To some extent make sure that we are dealing with a list from +  // "getmatches()". +  int i = 0; +  TV_LIST_ITER_CONST(l, li, { +    if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT +        || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { +      emsgf(_("E474: List item %d is either not a dictionary " +              "or an empty one"), i); +      return;      } +    if (!(tv_dict_find(d, S_LEN("group")) != NULL +          && (tv_dict_find(d, S_LEN("pattern")) != NULL +              || tv_dict_find(d, S_LEN("pos1")) != NULL) +          && tv_dict_find(d, S_LEN("priority")) != NULL +          && tv_dict_find(d, S_LEN("id")) != NULL)) { +      emsgf(_("E474: List item %d is missing one of the required keys"), i); +      return; +    } +    i++; +  }); -    clear_matches(curwin); -    li = l->lv_first; -    bool match_add_failed = false; -    while (li != NULL) { -      int i = 0; - -      d = li->li_tv.vval.v_dict; -      dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); -      if (di == NULL) { -        if (s == NULL) { -          s = tv_list_alloc(); -        } +  clear_matches(curwin); +  bool match_add_failed = false; +  TV_LIST_ITER_CONST(l, li, { +    int i = 0; -        // match from matchaddpos() -        for (i = 1; i < 9; i++) { -          char buf[5]; -          snprintf(buf, sizeof(buf), "pos%d", i); -          dictitem_T *const pos_di = tv_dict_find(d, buf, -1); -          if (pos_di != NULL) { -            if (pos_di->di_tv.v_type != VAR_LIST) { -              return; -            } +    d = TV_LIST_ITEM_TV(li)->vval.v_dict; +    dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); +    if (di == NULL) { +      if (s == NULL) { +        s = tv_list_alloc(9); +      } -            tv_list_append_tv(s, &pos_di->di_tv); -            s->lv_refcount++; -          } else { -            break; +      // match from matchaddpos() +      for (i = 1; i < 9; i++) { +        char buf[5]; +        snprintf(buf, sizeof(buf), "pos%d", i); +        dictitem_T *const pos_di = tv_dict_find(d, buf, -1); +        if (pos_di != NULL) { +          if (pos_di->di_tv.v_type != VAR_LIST) { +            return;            } -        } -      } -      // Note: there are three number buffers involved: -      // - group_buf below. -      // - numbuf in tv_dict_get_string(). -      // - mybuf in tv_get_string(). -      // -      // If you change this code make sure that buffers will not get -      // accidentally reused. -      char group_buf[NUMBUFLEN]; -      const char *const group = tv_dict_get_string_buf(d, "group", group_buf); -      const int priority = (int)tv_dict_get_number(d, "priority"); -      const int id = (int)tv_dict_get_number(d, "id"); -      dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); -      const char *const conceal = (conceal_di != NULL -                                   ? tv_get_string(&conceal_di->di_tv) -                                   : NULL); -      if (i == 0) { -        if (match_add(curwin, group, -                      tv_dict_get_string(d, "pattern", false), -                      priority, id, NULL, conceal) != id) { -          match_add_failed = true; -        } -      } else { -        if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { -          match_add_failed = true; +          tv_list_append_tv(s, &pos_di->di_tv); +          tv_list_ref(s); +        } else { +          break;          } -        tv_list_unref(s); -        s = NULL;        } -      li = li->li_next;      } -    if (!match_add_failed) { -      rettv->vval.v_number = 0; + +    // Note: there are three number buffers involved: +    // - group_buf below. +    // - numbuf in tv_dict_get_string(). +    // - mybuf in tv_get_string(). +    // +    // If you change this code make sure that buffers will not get +    // accidentally reused. +    char group_buf[NUMBUFLEN]; +    const char *const group = tv_dict_get_string_buf(d, "group", group_buf); +    const int priority = (int)tv_dict_get_number(d, "priority"); +    const int id = (int)tv_dict_get_number(d, "id"); +    dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); +    const char *const conceal = (conceal_di != NULL +                                 ? tv_get_string(&conceal_di->di_tv) +                                 : NULL); +    if (i == 0) { +      if (match_add(curwin, group, +                    tv_dict_get_string(d, "pattern", false), +                    priority, id, NULL, conceal) != id) { +        match_add_failed = true; +      } +    } else { +      if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { +        match_add_failed = true; +      } +      tv_list_unref(s); +      s = NULL;      } +  }); +  if (!match_add_failed) { +    rettv->vval.v_number = 0;    }  } @@ -14860,7 +14906,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)    if (argvars[1].v_type == VAR_LIST) {      list_T *ll = argvars[1].vval.v_list;      // If the list is NULL handle like an empty list. -    int len = ll == NULL ? 0 : ll->lv_len; +    const int len = tv_list_len(ll);      // First half: use for pointers to result lines; second half: use for      // pointers to allocated copies. @@ -14869,11 +14915,9 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)      char **allocval = lstval + len + 2;      char **curallocval = allocval; -    for (listitem_T *li = ll == NULL ? NULL : ll->lv_first; -         li != NULL; -         li = li->li_next) { +    TV_LIST_ITER_CONST(ll, li, {        char buf[NUMBUFLEN]; -      *curval = tv_get_string_buf_chk(&li->li_tv, buf); +      *curval = tv_get_string_buf_chk(TV_LIST_ITEM_TV(li), buf);        if (*curval == NULL) {          goto free_lstval;        } @@ -14885,7 +14929,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)          curallocval++;        }        curval++; -    } +    });      *curval++ = NULL;      write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, @@ -15106,12 +15150,6 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)    rettv->v_type = VAR_NUMBER;  } -/// struct used in the array that's given to qsort() -typedef struct { -  listitem_T *item; -  int idx; -} sortItem_T; -  /// struct storing information about current sort  typedef struct {    int item_compare_ic; @@ -15132,11 +15170,11 @@ static sortinfo_T *sortinfo = NULL;   */  static int item_compare(const void *s1, const void *s2, bool keep_zero)  { -  sortItem_T *const si1 = (sortItem_T *)s1; -  sortItem_T *const si2 = (sortItem_T *)s2; +  ListSortItem *const si1 = (ListSortItem *)s1; +  ListSortItem *const si2 = (ListSortItem *)s2; -  typval_T *const tv1 = &si1->item->li_tv; -  typval_T *const tv2 = &si2->item->li_tv; +  typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); +  typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item);    int res; @@ -15208,6 +15246,8 @@ item_compare_end:    // When the result would be zero, compare the item indexes.  Makes the    // sort stable.    if (res == 0 && !keep_zero) { +    // WARNING: When using uniq si1 and si2 are actually listitem_T **, no +    // indexes are there.      res = si1->idx > si2->idx ? 1 : -1;    }    return res; @@ -15225,7 +15265,7 @@ static int item_compare_not_keeping_zero(const void *s1, const void *s2)  static int item_compare2(const void *s1, const void *s2, bool keep_zero)  { -  sortItem_T  *si1, *si2; +  ListSortItem *si1, *si2;    int res;    typval_T rettv;    typval_T argv[3]; @@ -15238,8 +15278,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)      return 0;    } -  si1 = (sortItem_T *)s1; -  si2 = (sortItem_T *)s2; +  si1 = (ListSortItem *)s1; +  si2 = (ListSortItem *)s2;    if (partial == NULL) {      func_name = sortinfo->item_compare_func; @@ -15249,8 +15289,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)    // Copy the values.  This is needed to be able to set v_lock to VAR_FIXED    // in the copy without changing the original list items. -  tv_copy(&si1->item->li_tv, &argv[0]); -  tv_copy(&si2->item->li_tv, &argv[1]); +  tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]); +  tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]);    rettv.v_type = VAR_UNKNOWN;  // tv_clear() uses this    res = call_func((const char_u *)func_name, @@ -15273,6 +15313,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)    // When the result would be zero, compare the pointers themselves.  Makes    // the sort stable.    if (res == 0 && !keep_zero) { +    // WARNING: When using uniq si1 and si2 are actually listitem_T **, no +    // indexes are there.      res = si1->idx > si2->idx ? 1 : -1;    } @@ -15294,9 +15336,7 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2)   */  static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)  { -  list_T      *l; -  listitem_T  *li; -  sortItem_T  *ptrs; +  ListSortItem  *ptrs;    long len;    long i; @@ -15313,13 +15353,13 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)    if (argvars[0].v_type != VAR_LIST) {      EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");    } else { -    l = argvars[0].vval.v_list; -    if (l == NULL || tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) { -        goto theend; +    list_T *const l = argvars[0].vval.v_list; +    if (tv_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { +      goto theend;      }      rettv->vval.v_list = l;      rettv->v_type = VAR_LIST; -    ++l->lv_refcount; +    tv_list_ref(l);      len = tv_list_len(l);      if (len <= 1) { @@ -15385,80 +15425,45 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)        }      } -    /* Make an array with each entry pointing to an item in the List. */ -    ptrs = xmalloc((size_t)(len * sizeof (sortItem_T))); +    // Make an array with each entry pointing to an item in the List. +    ptrs = xmalloc((size_t)(len * sizeof(ListSortItem))); -    i = 0;      if (sort) { -      // sort(): ptrs will be the list to sort. -      for (li = l->lv_first; li != NULL; li = li->li_next) { -        ptrs[i].item = li; -        ptrs[i].idx = i; -        i++; -      } -        info.item_compare_func_err = false; -      // Test the compare function. -      if ((info.item_compare_func != NULL -           || info.item_compare_partial != NULL) -          && item_compare2_not_keeping_zero(&ptrs[0], &ptrs[1]) -             == ITEM_COMPARE_FAIL) { +      tv_list_item_sort(l, ptrs, +                        ((info.item_compare_func == NULL +                          && info.item_compare_partial == NULL) +                         ? item_compare_not_keeping_zero +                         : item_compare2_not_keeping_zero), +                        &info.item_compare_func_err); +      if (info.item_compare_func_err) {          EMSG(_("E702: Sort compare function failed")); -      } else { -        // Sort the array with item pointers. -        qsort(ptrs, (size_t)len, sizeof (sortItem_T), -              (info.item_compare_func == NULL -               && info.item_compare_partial == NULL ? -               item_compare_not_keeping_zero : -               item_compare2_not_keeping_zero)); - -        if (!info.item_compare_func_err) { -          // Clear the list and append the items in the sorted order. -          l->lv_first    = NULL; -          l->lv_last     = NULL; -          l->lv_idx_item = NULL; -          l->lv_len      = 0; - -          for (i = 0; i < len; i++) { -            tv_list_append(l, ptrs[i].item); -          } -        }        }      } else { -      int (*item_compare_func_ptr)(const void *, const void *); +      ListSorter item_compare_func_ptr;        // f_uniq(): ptrs will be a stack of items to remove.        info.item_compare_func_err = false;        if (info.item_compare_func != NULL            || info.item_compare_partial != NULL) { -          item_compare_func_ptr = item_compare2_keeping_zero; +        item_compare_func_ptr = item_compare2_keeping_zero;        } else { -          item_compare_func_ptr = item_compare_keeping_zero; +        item_compare_func_ptr = item_compare_keeping_zero;        } -      for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) { -        if (item_compare_func_ptr(&li, &li->li_next) == 0) { -          ptrs[i++].item = li; -        } -        if (info.item_compare_func_err) { -          EMSG(_("E882: Uniq compare function failed")); -          break; -        } -      } - -      if (!info.item_compare_func_err) { -        while (--i >= 0) { -          assert(ptrs[i].item->li_next); -          li = ptrs[i].item->li_next; -          ptrs[i].item->li_next = li->li_next; -          if (li->li_next != NULL) { -            li->li_next->li_prev = ptrs[i].item; -          } else { -            l->lv_last = ptrs[i].item; +      int idx = 0; +      for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) +           ; li != NULL;) { +        listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); +        if (item_compare_func_ptr(&prev_li, &li) == 0) { +          if (info.item_compare_func_err) { +            EMSG(_("E882: Uniq compare function failed")); +            break;            } -          tv_list_watch_fix(l, li); -          tv_list_item_free(li); -          l->lv_len--; +          li = tv_list_item_remove(l, li); +        } else { +          idx++; +          li = TV_LIST_ITEM_NEXT(l, li);          }        }      } @@ -15549,13 +15554,12 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)    hlf_T attr = HLF_COUNT;    size_t len = 0; -  tv_list_alloc_ret(rettv); -    if (argvars[0].v_type == VAR_UNKNOWN) {      // Find the start and length of the badly spelled word.      len = spell_move_to(curwin, FORWARD, true, true, &attr);      if (len != 0) {        word = (char *)get_cursor_pos_ptr(); +      curwin->w_set_curswant = true;      }    } else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) {      const char *str = tv_get_string_chk(&argvars[0]); @@ -15575,6 +15579,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)    }    assert(len <= INT_MAX); +  tv_list_alloc_ret(rettv, 2);    tv_list_append_string(rettv->vval.v_list, word, len);    tv_list_append_string(rettv->vval.v_list,                          (attr == HLF_SPB ? "bad" @@ -15591,41 +15596,36 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)  {    bool typeerr = false;    int maxcount; -  garray_T ga; -  listitem_T  *li; +  garray_T ga = GA_EMPTY_INIT_VALUE;    bool need_capital = false; -  tv_list_alloc_ret(rettv); -    if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) {      const char *const str = tv_get_string(&argvars[0]);      if (argvars[1].v_type != VAR_UNKNOWN) {        maxcount = tv_get_number_chk(&argvars[1], &typeerr);        if (maxcount <= 0) { -        return; +        goto f_spellsuggest_return;        }        if (argvars[2].v_type != VAR_UNKNOWN) {          need_capital = tv_get_number_chk(&argvars[2], &typeerr);          if (typeerr) { -          return; +          goto f_spellsuggest_return;          }        } -    } else +    } else {        maxcount = 25; +    }      spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false); +  } -    for (int i = 0; i < ga.ga_len; i++) { -      char *p = ((char **)ga.ga_data)[i]; - -      li = tv_list_item_alloc(); -      li->li_tv.v_type = VAR_STRING; -      li->li_tv.v_lock = 0; -      li->li_tv.vval.v_string = (char_u *)p; -      tv_list_append(rettv->vval.v_list, li); -    } -    ga_clear(&ga); +f_spellsuggest_return: +  tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len); +  for (int i = 0; i < ga.ga_len; i++) { +    char *const p = ((char **)ga.ga_data)[i]; +    tv_list_append_allocated_string(rettv->vval.v_list, p);    } +  ga_clear(&ga);  }  static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -15657,10 +15657,11 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)      pat = "[\\x01- ]\\+";    } -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, kListLenMayKnow); -  if (typeerr) +  if (typeerr) {      return; +  }    regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING);    if (regmatch.regprog != NULL) { @@ -15677,7 +15678,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)        } else {          end = str + strlen(str);        } -      if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 +      if (keepempty || end > str || (tv_list_len(rettv->vval.v_list) > 0                                       && *str != NUL                                       && match                                       && end < (const char *)regmatch.endp[0])) { @@ -16223,7 +16224,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)        break;      }      case 'n': {  // name -      p = get_highlight_name(NULL, id - 1); +      p = get_highlight_name_ext(NULL, id - 1, false);        break;      }      case 'r': {  // reverse @@ -16287,7 +16288,6 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)    memset(str, NUL, sizeof(str)); -  tv_list_alloc_ret(rettv);    if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0        && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) {      (void)syn_get_id(curwin, lnum, col, false, NULL, false); @@ -16296,18 +16296,16 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)      // get the conceal character      if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) {        cchar = syn_get_sub_char(); -      if (cchar == NUL && curwin->w_p_cole == 1 && lcs_conceal != NUL) { -        cchar = lcs_conceal; +      if (cchar == NUL && curwin->w_p_cole == 1) { +        cchar = (lcs_conceal == NUL) ? ' ' : lcs_conceal;        }        if (cchar != NUL) { -        if (has_mbyte) -          (*mb_char2bytes)(cchar, str); -        else -          str[0] = cchar; +        utf_char2bytes(cchar, str);        }      }    } +  tv_list_alloc_ret(rettv, 3);    tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0);    // -1 to auto-determine strlen    tv_list_append_string(rettv->vval.v_list, (const char *)str, -1); @@ -16330,7 +16328,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)        && lnum <= curbuf->b_ml.ml_line_count        && col >= 0        && (size_t)col <= STRLEN(ml_get(lnum))) { -    tv_list_alloc_ret(rettv); +    tv_list_alloc_ret(rettv, kListLenMayKnow);      (void)syn_get_id(curwin, lnum, col, false, NULL, true);      int id; @@ -16346,7 +16344,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty)    if (!keepempty && str[len - 1] == NL) {      len--;    } -  list_T *const list = tv_list_alloc(); +  list_T *const list = tv_list_alloc(kListLenMayKnow);    encode_list_write(list, str, len);    return list;  } @@ -16392,7 +16390,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,    if (res == NULL) {      if (retlist) {        // return an empty list when there's no output -      tv_list_alloc_ret(rettv); +      tv_list_alloc_ret(rettv, 0);      } else {        rettv->vval.v_string = (char_u *) xstrdup("");      } @@ -16405,7 +16403,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,        keepempty = tv_get_number(&argvars[2]);      }      rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); -    rettv->vval.v_list->lv_refcount++; +    tv_list_ref(rettv->vval.v_list);      rettv->v_type = VAR_LIST;      xfree(res); @@ -16458,7 +16456,7 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)      }    }    if (wp != NULL) { -    tv_list_alloc_ret(rettv); +    tv_list_alloc_ret(rettv, kListLenMayKnow);      while (wp != NULL) {        tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum);        wp = wp->w_next; @@ -16556,7 +16554,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)    char *fname;    tagname_T tn; -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, kListLenUnknown);    fname = xmalloc(MAXPATHL);    bool first = true; @@ -16585,8 +16583,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)    if (argvars[1].v_type != VAR_UNKNOWN) {      fname = tv_get_string(&argvars[1]);    } -  (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern, -                 (char_u *)fname); +  (void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), +                 (char_u *)tag_pattern, (char_u *)fname);  }  /* @@ -16692,6 +16690,18 @@ static void f_test_garbagecollect_now(typval_T *argvars,    garbage_collect(true);  } +// "test_write_list_log()" function +static void f_test_write_list_log(typval_T *const argvars, +                                  typval_T *const rettv, +                                  FunPtr fptr) +{ +  const char *const fname = tv_get_string_chk(&argvars[0]); +  if (fname == NULL) { +    return; +  } +  list_write_log(fname); +} +  bool callback_from_typval(Callback *const callback, typval_T *const arg)    FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT  { @@ -16827,7 +16837,9 @@ static void add_timer_info_all(typval_T *rettv)  /// "timer_info([timer])" function  static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, (argvars[0].v_type != VAR_UNKNOWN +                            ? 1 +                            : timers->table->n_occupied));    if (argvars[0].v_type != VAR_UNKNOWN) {      if (argvars[0].v_type != VAR_NUMBER) {        EMSG(_(e_number_exp)); @@ -16852,6 +16864,12 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr)    int paused = (bool)tv_get_number(&argvars[1]);    timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0]));    if (timer != NULL) { +    if (!timer->paused && paused) { +      time_watcher_stop(&timer->tw); +    } else if (timer->paused && !paused) { +      time_watcher_start(&timer->tw, timer_due_cb, timer->timeout, +                         timer->timeout); +    }      timer->paused = paused;    }  } @@ -17151,7 +17169,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)        break;      }      case VAR_UNKNOWN: { -      EMSG2(_(e_intern2), "f_type(UNKNOWN)"); +      internal_error("f_type(UNKNOWN)");        break;      }    } @@ -17187,7 +17205,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)    tv_dict_alloc_ret(rettv);    dict_T *dict = rettv->vval.v_dict; -  list_T *list;    tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced);    tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last); @@ -17197,9 +17214,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)    tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur);    tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur); -  list = tv_list_alloc(); -  u_eval_tree(curbuf->b_u_oldhead, list); -  tv_dict_add_list(dict, S_LEN("entries"), list); +  tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead));  }  /* @@ -17258,7 +17273,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr)  /// "win_findbuf()" function  static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  tv_list_alloc_ret(rettv); +  tv_list_alloc_ret(rettv, kListLenMayKnow);    win_findbuf(argvars, rettv->vval.v_list);  } @@ -17277,8 +17292,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr)  /// "win_id2tabwin()" function  static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr)  { -  tv_list_alloc_ret(rettv); -  win_id2tabwin(argvars, rettv->vval.v_list); +  win_id2tabwin(argvars, rettv);  }  /// "win_id2win()" function @@ -17449,10 +17463,11 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr)  /// @return true in case of success, false otherwise.  static bool write_list(FileDescriptor *const fp, const list_T *const list,                         const bool binary) +  FUNC_ATTR_NONNULL_ARG(1)  {    int error = 0; -  for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { -    const char *const s = tv_get_string_chk(&li->li_tv); +  TV_LIST_ITER_CONST(list, li, { +    const char *const s = tv_get_string_chk(TV_LIST_ITEM_TV(li));      if (s == NULL) {        return false;      } @@ -17479,14 +17494,14 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list,          }        }      } -    if (!binary || li->li_next != NULL) { +    if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) {        const ptrdiff_t written = file_write(fp, "\n", 1);        if (written < 0) {          error = (int)written;          goto write_list_error;        }      } -  } +  });    if ((error = file_flush(fp)) != 0) {      goto write_list_error;    } @@ -17496,56 +17511,29 @@ write_list_error:    return false;  } -/// Initializes a static list with 10 items. -void init_static_list(staticList10_T *sl) -{ -  list_T *l = &sl->sl_list; - -  memset(sl, 0, sizeof(staticList10_T)); -  l->lv_first = &sl->sl_items[0]; -  l->lv_last = &sl->sl_items[9]; -  l->lv_refcount = DO_NOT_FREE_CNT; -  l->lv_lock = VAR_FIXED; -  sl->sl_list.lv_len = 10; - -  for (int i = 0; i < 10; i++) { -    listitem_T *li = &sl->sl_items[i]; - -    if (i == 0) { -      li->li_prev = NULL; -    } else { -      li->li_prev = li - 1; -    } -    if (i == 9) { -      li->li_next = NULL; -    } else { -      li->li_next = li + 1; -    } -  } -} -  /// Saves a typval_T as a string.  /// -/// For lists, replaces NLs with NUL and separates items with NLs. +/// For lists or buffers, replaces NLs with NUL and separates items with NLs.  /// -/// @param[in]  tv   A value to store as a string. -/// @param[out] len  The length of the resulting string or -1 on error. +/// @param[in]  tv   Value to store as a string. +/// @param[out] len  Length of the resulting string or -1 on error.  /// @param[in]  endnl If true, the output will end in a newline (if a list).  /// @returns an allocated string if `tv` represents a VimL string, list, or  ///          number; NULL otherwise.  static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)    FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL  { +  *len = 0;    if (tv->v_type == VAR_UNKNOWN) { -    *len = 0;      return NULL;    } -  // For types other than list, let tv_get_string_buf_chk() get the value or +  // For other types, let tv_get_string_buf_chk() get the value or    // print an error. -  if (tv->v_type != VAR_LIST) { +  if (tv->v_type != VAR_LIST && tv->v_type != VAR_NUMBER) {      const char *ret = tv_get_string_chk(tv); -    if (ret && (*len = strlen(ret))) { +    if (ret) { +      *len = strlen(ret);        return xmemdupz(ret, (size_t)(*len));      } else {        *len = -1; @@ -17553,12 +17541,44 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)      }    } +  if (tv->v_type == VAR_NUMBER) {  // Treat number as a buffer-id. +    buf_T *buf = buflist_findnr(tv->vval.v_number); +    if (buf) { +      for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { +        for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { +          *len += 1; +        } +        *len += 1; +      } +    } else { +      EMSGN(_(e_nobufnr), tv->vval.v_number); +      *len = -1; +      return NULL; +    } + +    if (*len == 0) { +      return NULL; +    } + +    char *ret = xmalloc(*len + 1); +    char *end = ret; +    for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { +      for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { +        *end++ = (*p == '\n') ? NUL : *p; +      } +      *end++ = '\n'; +    } +    *end = NUL; +    *len = end - ret; +    return ret; +  } + +  assert(tv->v_type == VAR_LIST);    // Pre-calculate the resulting length. -  *len = 0;    list_T *list = tv->vval.v_list; -  for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { -    *len += strlen(tv_get_string(&li->li_tv)) + 1; -  } +  TV_LIST_ITER_CONST(list, li, { +    *len += strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1; +  });    if (*len == 0) {      return NULL; @@ -17566,14 +17586,14 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)    char *ret = xmalloc(*len + endnl);    char *end = ret; -  for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { -    for (const char *s = tv_get_string(&li->li_tv); *s != NUL; s++) { +  TV_LIST_ITER_CONST(list, li, { +    for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) {        *end++ = (*s == '\n') ? NUL : *s;      } -    if (endnl || li->li_next != NULL) { +    if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) {        *end++ = '\n';      } -  } +  });    *end = NUL;    *len = end - ret;    return ret; @@ -17613,9 +17633,6 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)      EMSG2(_(e_listarg), "writefile()");      return;    } -  if (argvars[0].vval.v_list == NULL) { -    return; -  }    bool binary = false;    bool append = false; @@ -17720,9 +17737,9 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,      // We accept "$" for the column number: last column.      li = tv_list_find(l, 1L); -    if (li != NULL && li->li_tv.v_type == VAR_STRING -        && li->li_tv.vval.v_string != NULL -        && STRCMP(li->li_tv.vval.v_string, "$") == 0) { +    if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING +        && TV_LIST_ITEM_TV(li)->vval.v_string != NULL +        && STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) {        pos.col = len + 1;      } @@ -17799,17 +17816,18 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum,   */  static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp)  { -  list_T      *l = arg->vval.v_list; +  list_T *l;    long i = 0;    long n; -  /* List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only -   * there when "fnump" isn't NULL; "coladd" and "curswant" are optional. */ +  // List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only +  // there when "fnump" isn't NULL; "coladd" and "curswant" are optional.    if (arg->v_type != VAR_LIST -      || l == NULL -      || l->lv_len < (fnump == NULL ? 2 : 3) -      || l->lv_len > (fnump == NULL ? 4 : 5)) +      || (l = arg->vval.v_list) == NULL +      || tv_list_len(l) < (fnump == NULL ? 2 : 3) +      || tv_list_len(l) > (fnump == NULL ? 4 : 5)) {      return FAIL; +  }    if (fnump != NULL) {      n = tv_list_find_nr(l, i++, NULL);  // fnum @@ -18234,7 +18252,7 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val)    vimvars[idx].vv_type = VAR_LIST;    vimvars[idx].vv_list = val;    if (val != NULL) { -    val->lv_refcount++; +    tv_list_ref(val);    }  } @@ -19030,7 +19048,7 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv,          }          return;        } else if (v->di_tv.v_type != tv->v_type) { -        EMSG2(_(e_intern2), "set_var()"); +        internal_error("set_var()");        }      } @@ -19050,6 +19068,9 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv,        return;      } +    // Make sure dict is valid +    assert(dict != NULL); +      v = xmalloc(sizeof(dictitem_T) + strlen(varname));      STRCPY(v->di_key, varname);      if (tv_dict_add(dict, v) == FAIL) { @@ -19267,12 +19288,12 @@ int var_item_copy(const vimconv_T *const conv,    case VAR_LIST:      to->v_type = VAR_LIST;      to->v_lock = 0; -    if (from->vval.v_list == NULL) +    if (from->vval.v_list == NULL) {        to->vval.v_list = NULL; -    else if (copyID != 0 && from->vval.v_list->lv_copyID == copyID) { -      /* use the copy made earlier */ -      to->vval.v_list = from->vval.v_list->lv_copylist; -      ++to->vval.v_list->lv_refcount; +    } else if (copyID != 0 && tv_list_copyid(from->vval.v_list) == copyID) { +      // Use the copy made earlier. +      to->vval.v_list = tv_list_latest_copy(from->vval.v_list); +      tv_list_ref(to->vval.v_list);      } else {        to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID);      } @@ -19297,7 +19318,7 @@ int var_item_copy(const vimconv_T *const conv,      }      break;    case VAR_UNKNOWN: -    EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)"); +    internal_error("var_item_copy(UNKNOWN)");      ret = FAIL;    }    --recurse; @@ -19365,14 +19386,10 @@ void ex_echo(exarg_T *eap)              }              msg_putchar_attr((uint8_t)(*p), echo_attr);            } else { -            if (has_mbyte) { -              int i = (*mb_ptr2len)((const char_u *)p); +            int i = (*mb_ptr2len)((const char_u *)p); -              (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr); -              p += i - 1; -            } else { -              (void)msg_outtrans_len_attr((char_u *)p, 1, echo_attr); -            } +            (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr); +            p += i - 1;            }          }        } @@ -20871,7 +20888,9 @@ void ex_delfunction(exarg_T *eap)    if (!eap->skip) {      if (fp == NULL) { -      EMSG2(_(e_nofunc), eap->arg); +      if (!eap->forceit) { +        EMSG2(_(e_nofunc), eap->arg); +      }        return;      }      if (fp->uf_calls > 0) { @@ -20985,11 +21004,11 @@ void func_unref(char_u *name)    if (fp == NULL && isdigit(*name)) {  #ifdef EXITFREE      if (!entered_free_all_mem) { -      EMSG2(_(e_intern2), "func_unref()"); +      internal_error("func_unref()");        abort();      }  #else -      EMSG2(_(e_intern2), "func_unref()"); +      internal_error("func_unref()");        abort();  #endif    } @@ -21028,7 +21047,7 @@ void func_ref(char_u *name)    } else if (isdigit(*name)) {      // Only give an error for a numbered function.      // Fail silently, when named or lambda function isn't found. -    EMSG2(_(e_intern2), "func_ref()"); +    internal_error("func_ref()");    }  } @@ -21040,6 +21059,22 @@ void func_ptr_ref(ufunc_T *fp)    }  } +/// Check whether funccall is still referenced outside +/// +/// It is supposed to be referenced if either it is referenced itself or if l:, +/// a: or a:000 are referenced as all these are statically allocated within +/// funccall structure. +static inline bool fc_referenced(const funccall_T *const fc) +  FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +  FUNC_ATTR_NONNULL_ALL +{ +  return ((fc->l_varlist.lv_refcount  // NOLINT(runtime/deprecated) +           != DO_NOT_FREE_CNT) +          || fc->l_vars.dv_refcount != DO_NOT_FREE_CNT +          || fc->l_avars.dv_refcount != DO_NOT_FREE_CNT +          || fc->fc_refcount > 0); +} +  /// Call a user function  ///  /// @param  fp  Function to call. @@ -21153,9 +21188,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,    v->di_tv.v_type = VAR_LIST;    v->di_tv.v_lock = VAR_FIXED;    v->di_tv.vval.v_list = &fc->l_varlist; -  memset(&fc->l_varlist, 0, sizeof(list_T)); -  fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT; -  fc->l_varlist.lv_lock = VAR_FIXED; +  tv_list_init_static(&fc->l_varlist); +  tv_list_set_lock(&fc->l_varlist, VAR_FIXED);    // Set a:firstline to "firstline" and a:lastline to "lastline".    // Set a:name to named arguments. @@ -21204,8 +21238,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,      if (ai >= 0 && ai < MAX_FUNC_ARGS) {        tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]); -      fc->l_listitems[ai].li_tv = argvars[i]; -      fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; +      *TV_LIST_ITEM_TV(&fc->l_listitems[ai]) = argvars[i]; +      TV_LIST_ITEM_TV(&fc->l_listitems[ai])->v_lock = VAR_FIXED;      }    } @@ -21275,15 +21309,17 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,      }    } +  const bool do_profiling_yes = do_profiling == PROF_YES; +    bool func_not_yet_profiling_but_should = -    do_profiling == PROF_YES -    && !fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL); +    do_profiling_yes +    && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL);    if (func_not_yet_profiling_but_should)      func_do_profile(fp);    bool func_or_func_caller_profiling = -    do_profiling == PROF_YES +    do_profiling_yes      && (fp->uf_profiling          || (fc->caller != NULL && fc->caller->func->uf_profiling)); @@ -21293,7 +21329,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,      fp->uf_tm_children = profile_zero();    } -  if (do_profiling == PROF_YES) { +  if (do_profiling_yes) {      script_prof_save(&wait_start);    } @@ -21369,8 +21405,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,    sourcing_name = save_sourcing_name;    sourcing_lnum = save_sourcing_lnum;    current_SID = save_current_SID; -  if (do_profiling == PROF_YES) +  if (do_profiling_yes) {      script_prof_restore(&wait_start); +  }    if (p_verbose >= 12 && sourcing_name != NULL) {      ++no_wait_return; @@ -21389,10 +21426,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,    // If the a:000 list and the l: and a: dicts are not referenced and there    // is no closure using it, we can free the funccall_T and what's in it. -  if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT -      && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT -      && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT -      && fc->fc_refcount <= 0) { +  if (!fc_referenced(fc)) {      free_funccal(fc, false);    } else {      // "fc" is still in use.  This can happen when returning "a:000", @@ -21407,10 +21441,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars,      });      // Make a copy of the a:000 items, since we didn't do that above. -    for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; -         li = li->li_next) { -      tv_copy(&li->li_tv, &li->li_tv); -    } +    TV_LIST_ITER(&fc->l_varlist, li, { +      tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li)); +    });    }    if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { @@ -21437,10 +21470,8 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force)      return;    } -  if (--fc->fc_refcount <= 0 && (force || ( -      fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT -      && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT -      && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT))) { +  fc->fc_refcount--; +  if (force ? fc->fc_refcount <= 0 : !fc_referenced(fc)) {      for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) {        if (fc == *pfc) {          *pfc = fc->caller; @@ -21475,8 +21506,6 @@ free_funccal (      int free_val              /* a: vars were allocated */  )  { -  listitem_T  *li; -    for (int i = 0; i < fc->fc_funcs.ga_len; i++) {      ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i]; @@ -21494,14 +21523,14 @@ free_funccal (    // allocated variables.    vars_clear_ext(&fc->l_avars.dv_hashtab, free_val); -  /* free all l: variables */ +  // Free all l: variables.    vars_clear(&fc->l_vars.dv_hashtab);    // Free the a:000 variables if they were allocated.    if (free_val) { -    for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { -      tv_clear(&li->li_tv); -    } +    TV_LIST_ITER(&fc->l_varlist, li, { +      tv_clear(TV_LIST_ITEM_TV(li)); +    });    }    func_ptr_unref(fc->func); @@ -22407,7 +22436,7 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)      return;    } -  list_T *args = tv_list_alloc(); +  list_T *args = tv_list_alloc(1);    tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1);    *rettv = eval_call_provider(name, "eval", args);  } @@ -22425,7 +22454,6 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)      .sourcing_lnum = sourcing_lnum,      .autocmd_fname = autocmd_fname,      .autocmd_match = autocmd_match, -    .autocmd_fname_full = autocmd_fname_full,      .autocmd_bufnr = autocmd_bufnr,      .funccalp = save_funccal()    }; @@ -22436,8 +22464,8 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)      {.v_type = VAR_LIST, .vval.v_list = arguments, .v_lock = 0},      {.v_type = VAR_UNKNOWN}    }; -  typval_T rettv = {.v_type = VAR_UNKNOWN, .v_lock = 0}; -  arguments->lv_refcount++; +  typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; +  tv_list_ref(arguments);    int dummy;    (void)call_func((const char_u *)func, @@ -22458,7 +22486,8 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)    restore_funccal(provider_caller_scope.funccalp);    provider_caller_scope = saved_provider_caller_scope;    provider_call_nesting--; -   +  assert(provider_call_nesting >= 0); +    return rettv;  } @@ -22498,11 +22527,13 @@ bool eval_has_provider(const char *name)  }  /// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`. -void eval_format_source_name_line(char *buf, size_t bufsize) +void eval_fmt_source_name_line(char *buf, size_t bufsize)  { -  snprintf(buf, bufsize, "%s:%" PRIdLINENR, -           (sourcing_name ? sourcing_name : (char_u *)"?"), -           (sourcing_name ? sourcing_lnum : 0)); +  if (sourcing_name) { +    snprintf(buf, bufsize, "%s:%" PRIdLINENR, sourcing_name, sourcing_lnum); +  } else { +    snprintf(buf, bufsize, "?"); +  }  }  /// ":checkhealth [plugins]"  | 
