diff options
| author | Justin M. Keyes <justinkz@gmail.com> | 2017-12-23 18:17:01 +0100 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-12-23 18:17:01 +0100 | 
| commit | dee78a4095a27369e428572f74f7b64bcc5f670e (patch) | |
| tree | 4f06de0fd7a5a80d746c2ffaf18cb0719e66cccd /src/nvim/eval.c | |
| parent | ec86f4215fc58246998c6017df84206153d0df1a (diff) | |
| parent | 5cb7a709e7f60b0e7bcde70a1aa9fea5f060fe0f (diff) | |
| download | rneovim-dee78a4095a27369e428572f74f7b64bcc5f670e.tar.gz rneovim-dee78a4095a27369e428572f74f7b64bcc5f670e.tar.bz2 rneovim-dee78a4095a27369e428572f74f7b64bcc5f670e.zip | |
Merge #7708 from ZyX-I/hide-container-impl
Diffstat (limited to 'src/nvim/eval.c')
| -rw-r--r-- | src/nvim/eval.c | 1202 | 
1 files changed, 589 insertions, 613 deletions
| diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2355c8c813..e6cb8cdec0 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. */ @@ -564,8 +561,8 @@ 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; +    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) { @@ -1014,7 +1011,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);          }        } @@ -1144,27 +1141,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);  } @@ -1505,9 +1506,7 @@ ex_let_vars (  )  {    char_u *arg = arg_start; -  list_T      *l;    int i; -  listitem_T  *item;    typval_T ltv;    if (*arg != '[') { @@ -1519,13 +1518,12 @@ 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) { @@ -1536,29 +1534,34 @@ ex_let_vars (      EMSG(_("E688: More targets than List items"));      return FAIL;    } +  // 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);    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); +    item = TV_LIST_ITEM_NEXT(l, item); +    if (arg == NULL) {        return FAIL; +    }      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();        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.vval.v_list = rest_list; +      tv_list_ref(rest_list);        arg = ex_let_one(skipwhite(arg + 1), <v, false,                         (char_u *)"]", nextchars); @@ -2311,7 +2314,7 @@ 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);      }    } @@ -2376,47 +2379,52 @@ 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) { +      } +      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); +        assert(TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li));        } -      lp->ll_li = lp->ll_li->li_next; -      ++lp->ll_n1; +      lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); +      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; @@ -2515,7 +2523,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);        }      }    } @@ -2533,21 +2541,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 @@ -2871,7 +2876,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, @@ -2884,8 +2892,9 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)      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, +      li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); +      if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, +                        (const char *)lp->ll_name,                          lp->ll_name_len)) {          return false;        } @@ -2893,12 +2902,12 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)        ll_n1++;      } -    /* Delete a range of List items. */ +    // 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; +      li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);        tv_list_item_remove(lp->ll_list, lp->ll_li);        lp->ll_li = li; -      ++lp->ll_n1; +      lp->ll_n1++;      }    } else {      if (lp->ll_list != NULL) { @@ -3044,13 +3053,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); @@ -4512,15 +4521,15 @@ eval_index (            l = tv_list_alloc();            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(l, 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;          } @@ -4878,22 +4887,23 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)        goto failret;      if (evaluate) {        item = tv_list_item_alloc(); -      item->li_tv = tv; -      item->li_tv.v_lock = 0; +      *TV_LIST_ITEM_TV(item) = tv; +      TV_LIST_ITEM_TV(item)->v_lock = VAR_UNLOCKED;        tv_list_append(l, item);      } -    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); @@ -4905,7 +4915,7 @@ failret:    if (evaluate) {      rettv->v_type = VAR_LIST;      rettv->vval.v_list = l; -    ++l->lv_refcount; +    tv_list_ref(l);    }    return OK; @@ -5242,8 +5252,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. @@ -5263,7 +5273,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. @@ -5328,15 +5338,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; @@ -6533,12 +6544,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);      } @@ -6589,9 +6598,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) { @@ -6599,7 +6609,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. @@ -6611,7 +6621,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); @@ -7250,30 +7260,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]); @@ -7490,7 +7496,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;    } @@ -7598,7 +7604,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; @@ -7616,9 +7622,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; @@ -7936,34 +7944,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: -    internal_error("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; @@ -8027,17 +8052,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));  } @@ -8079,11 +8109,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; @@ -8265,14 +8298,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); @@ -8280,7 +8306,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); @@ -8289,8 +8315,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); @@ -8462,7 +8489,8 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)    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) { @@ -8525,16 +8553,18 @@ 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 (li = tv_list_first(l); li != NULL; li = nli) {          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; +        nli = TV_LIST_ITEM_NEXT(l, li);          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);          } @@ -8938,7 +8968,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;          }        } @@ -8949,7 +8979,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); @@ -8958,11 +8988,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++]); +          });          }        } @@ -9048,7 +9076,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) { @@ -10070,7 +10098,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)      if (rettv->vval.v_list == NULL) {        rettv->vval.v_list = tv_list_alloc();      } -    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); @@ -11027,39 +11055,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;        } +    }    }  } @@ -11223,11 +11254,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;    } @@ -11238,15 +11268,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;  } @@ -11302,9 +11333,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); @@ -11315,7 +11345,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); @@ -11379,7 +11409,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); @@ -11416,32 +11446,32 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,      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_LIST_ITEM_TV(li)->v_type = VAR_STRING; +        TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; +        TV_LIST_ITEM_TV(li)->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_LIST_ITEM_TV(li));          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++; +        TV_LIST_ITEM_TV(li)->v_type = VAR_LIST; +        TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; +        TV_LIST_ITEM_TV(li)->vval.v_list = sub_l; +        tv_list_ref(sub_l);          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); +        TV_LIST_ITEM_TV(sub_li)->v_type = VAR_STRING; +        TV_LIST_ITEM_TV(sub_li)->v_lock = VAR_UNLOCKED; +        TV_LIST_ITEM_TV(sub_li)->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); +        tv_copy(&di->di_tv, TV_LIST_ITEM_TV(sub_li));          break;        }      } @@ -11539,15 +11569,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; @@ -11562,15 +11590,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;  } @@ -11698,7 +11726,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); @@ -11707,10 +11735,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; @@ -11722,7 +11750,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; @@ -11731,7 +11760,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; @@ -11761,7 +11790,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)    list_T *rv = tv_list_alloc();    // 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; @@ -11777,7 +11806,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;  } @@ -11791,9 +11820,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])); @@ -12203,7 +12229,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)    if (argvars[0].v_type == VAR_LIST) {      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); @@ -12223,11 +12249,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; @@ -12263,7 +12289,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;          } @@ -12278,8 +12305,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); @@ -12292,18 +12319,22 @@ 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); +        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) { -          li2->li_tv.vval.v_number = (varnumber_T)idx; +          TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx;          }        } else if (type == 3) {          int i; @@ -12321,7 +12352,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)        } else if (type == 2) {          // Return matched string.          if (l != NULL) { -          tv_copy(&li->li_tv, rettv); +          tv_copy(TV_LIST_ITEM_TV(li), rettv);          } else {            rettv->vval.v_string = (char_u *)xmemdupz(                (const char *)regmatch.startp[0], @@ -12345,8 +12376,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)    if (type == 4 && 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: @@ -12412,56 +12443,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); @@ -12542,41 +12573,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;  }  /* @@ -12662,22 +12693,19 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr)      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 *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);  } @@ -12691,10 +12719,10 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)    }    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) { +  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;    } @@ -12735,9 +12763,9 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)        }        if (result == MSGPACK_UNPACK_SUCCESS) {          listitem_T *li = tv_list_item_alloc(); -        li->li_tv.v_type = VAR_UNKNOWN; +        TV_LIST_ITEM_TV(li)->v_type = VAR_UNKNOWN;          tv_list_append(ret_list, li); -        if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { +        if (msgpack_to_vim(unpacked.data, TV_LIST_ITEM_TV(li)) == FAIL) {            EMSG2(_(e_invarg2), "Failed to convert msgpack string");            goto f_msgpackparse_exit;          } @@ -13030,9 +13058,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)          }          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_ITEM_TV(li)->v_type = VAR_STRING; +        TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; +        TV_LIST_ITEM_TV(li)->vval.v_string = s;          tv_list_append(rettv->vval.v_list, li);          start = p + 1;         /* step over newline */ @@ -13109,7 +13137,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)     */    if (maxline < 0)      while (cnt > -maxline) { -      tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); +      tv_list_item_remove(rettv->vval.v_list, +                          tv_list_first(rettv->vval.v_list));        cnt--;      } @@ -13126,9 +13155,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;    } @@ -13252,8 +13279,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); @@ -13265,7 +13292,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)        if (argvars[2].v_type == VAR_UNKNOWN) {          // Remove one item, return its value.          tv_list_remove_items(l, item, item); -        *rettv = item->li_tv; +        *rettv = *TV_LIST_ITEM_TV(item);          xfree(item);        } else {          // Remove range of items, return list with values. @@ -13277,21 +13304,16 @@ 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);            }          }        } @@ -13527,21 +13549,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);    }  } @@ -13839,14 +13852,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) { @@ -13864,9 +13880,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 @@ -14295,9 +14311,9 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)    list_T *l = tv_list_alloc_ret(rettv);    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_ITEM_TV(li)->v_type = VAR_STRING; +    TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; +    TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)addrs[i];      tv_list_append(l, li);    }    xfree(addrs); @@ -14506,25 +14522,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. */ @@ -14626,8 +14643,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;    }  } @@ -14652,8 +14669,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; @@ -14662,92 +14677,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(); +      } -            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;    }  } @@ -14864,7 +14876,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. @@ -14873,11 +14885,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;        } @@ -14889,7 +14899,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, @@ -15139,8 +15149,8 @@ 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; -  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; @@ -15212,6 +15222,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; @@ -15253,8 +15265,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, @@ -15277,6 +15289,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;    } @@ -15298,8 +15312,6 @@ 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;    long len;    long i; @@ -15317,13 +15329,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) { @@ -15395,11 +15407,11 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)      i = 0;      if (sort) {        // sort(): ptrs will be the list to sort. -      for (li = l->lv_first; li != NULL; li = li->li_next) { +      TV_LIST_ITER(l, li, {          ptrs[i].item = li;          ptrs[i].idx = i;          i++; -      } +      });        info.item_compare_func_err = false;        // Test the compare function. @@ -15418,11 +15430,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)          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; - +          tv_list_clear(l);            for (i = 0; i < len; i++) {              tv_list_append(l, ptrs[i].item);            } @@ -15435,34 +15443,25 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)        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; -      } - -      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; -        } +        item_compare_func_ptr = item_compare_keeping_zero;        } -      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 +           ; li = TV_LIST_ITEM_NEXT(l, li)) { +        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--; +          tv_list_item_remove(l, li); +          li = tv_list_find(l, idx); +        } else { +          idx++;          }        }      } @@ -15596,7 +15595,6 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)    bool typeerr = false;    int maxcount;    garray_T ga; -  listitem_T  *li;    bool need_capital = false;    tv_list_alloc_ret(rettv); @@ -15622,10 +15620,10 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)      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; +      listitem_T *const li = tv_list_item_alloc(); +      TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; +      TV_LIST_ITEM_TV(li)->v_lock = VAR_LOCKED; +      TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)p;        tv_list_append(rettv->vval.v_list, li);      }      ga_clear(&ga); @@ -15681,7 +15679,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])) { @@ -16409,7 +16407,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); @@ -17453,10 +17451,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;      } @@ -17483,14 +17482,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;    } @@ -17500,34 +17499,6 @@ 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. @@ -17560,9 +17531,9 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl)    // 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; @@ -17570,14 +17541,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; @@ -17617,9 +17588,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; @@ -17724,9 +17692,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;      } @@ -17803,17 +17771,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 @@ -18238,7 +18207,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);    }  } @@ -19271,12 +19240,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);      } @@ -21044,6 +21013,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. @@ -21157,9 +21142,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. @@ -21208,8 +21192,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;      }    } @@ -21393,10 +21377,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", @@ -21411,10 +21392,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) { @@ -21441,10 +21421,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; @@ -21479,8 +21457,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]; @@ -21498,14 +21474,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); @@ -22440,8 +22416,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, | 
