Patch 8.2.4698
Problem:    Vim9: script variable has no flag that it was set.
Solution:   Add a flag that it was set, to avoid giving it a value when used.
            (closes #10088)
Files:      src/structs.h, src/vim9script.c, src/vim9execute.c,
            src/evalvars.c, src/testdir/test_vim9_assign.vim,
            src/testdir/test_vim9_builtin.vim


*** ../vim-8.2.4697/src/structs.h       2022-04-03 18:01:39.659574455 +0100
--- src/structs.h       2022-04-05 20:44:32.642160773 +0100
***************
*** 1807,1812 ****
--- 1807,1816 ----
  #define HIKEY2SAV(p)  ((sallvar_T *)(p - offsetof(sallvar_T, sav_key)))
  #define HI2SAV(hi)     HIKEY2SAV((hi)->hi_key)
  
+ #define SVFLAG_TYPE_ALLOCATED 1  // call free_type() for "sv_type"
+ #define SVFLAG_EXPORTED               2  // "export let var = val"
+ #define SVFLAG_ASSIGNED               4  // assigned a value
+ 
  /*
   * Entry for "sn_var_vals".  Used for script-local variables.
   */
***************
*** 1814,1822 ****
      char_u    *sv_name;       // points into "sn_all_vars" di_key
      typval_T  *sv_tv;         // points into "sn_vars" or "sn_all_vars" di_tv
      type_T    *sv_type;
!     int               sv_type_allocated;  // call free_type() for sv_type
      int               sv_const;       // 0, ASSIGN_CONST or ASSIGN_FINAL
-     int               sv_export;      // "export let var = val"
  };
  
  typedef struct {
--- 1818,1825 ----
      char_u    *sv_name;       // points into "sn_all_vars" di_key
      typval_T  *sv_tv;         // points into "sn_vars" or "sn_all_vars" di_tv
      type_T    *sv_type;
!     int               sv_flags;       // SVFLAG_ values above
      int               sv_const;       // 0, ASSIGN_CONST or ASSIGN_FINAL
  };
  
  typedef struct {
*** ../vim-8.2.4697/src/vim9script.c    2022-04-04 14:58:02.166539812 +0100
--- src/vim9script.c    2022-04-05 21:26:13.173914591 +0100
***************
*** 334,340 ****
      {
        svar_T    *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
  
!       if (sv->sv_type_allocated)
            free_type(sv->sv_type);
      }
      ga_clear(&si->sn_var_vals);
--- 334,340 ----
      {
        svar_T    *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx;
  
!       if (sv->sv_flags & SVFLAG_TYPE_ALLOCATED)
            free_type(sv->sv_type);
      }
      ga_clear(&si->sn_var_vals);
***************
*** 721,727 ****
      {
        sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
        *ufunc = NULL;
!       if (!sv->sv_export)
        {
            if (verbose)
                semsg(_(e_item_not_exported_in_script_str), name);
--- 721,727 ----
      {
        sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
        *ufunc = NULL;
!       if ((sv->sv_flags & SVFLAG_EXPORTED) == 0)
        {
            if (verbose)
                semsg(_(e_item_not_exported_in_script_str), name);
***************
*** 871,877 ****
   * with a hashtable) and sn_var_vals (lookup by index).
   * When "create" is TRUE this is a new variable, otherwise find and update an
   * existing variable.
!  * "flags" can have ASSIGN_FINAL or ASSIGN_CONST.
   * When "*type" is NULL use "tv" for the type and update "*type".  If
   * "do_member" is TRUE also use the member type, otherwise use "any".
   */
--- 871,877 ----
   * with a hashtable) and sn_var_vals (lookup by index).
   * When "create" is TRUE this is a new variable, otherwise find and update an
   * existing variable.
!  * "flags" can have ASSIGN_FINAL, ASSIGN_CONST or ASSIGN_INIT.
   * When "*type" is NULL use "tv" for the type and update "*type".  If
   * "do_member" is TRUE also use the member type, otherwise use "any".
   */
***************
*** 938,944 ****
            sv->sv_tv = &di->di_tv;
            sv->sv_const = (flags & ASSIGN_FINAL) ? ASSIGN_FINAL
                                   : (flags & ASSIGN_CONST) ? ASSIGN_CONST : 0;
!           sv->sv_export = is_export;
            newsav->sav_var_vals_idx = si->sn_var_vals.ga_len;
            ++si->sn_var_vals.ga_len;
            STRCPY(&newsav->sav_key, name);
--- 938,946 ----
            sv->sv_tv = &di->di_tv;
            sv->sv_const = (flags & ASSIGN_FINAL) ? ASSIGN_FINAL
                                   : (flags & ASSIGN_CONST) ? ASSIGN_CONST : 0;
!           sv->sv_flags = is_export ? SVFLAG_EXPORTED : 0;
!           if ((flags & ASSIGN_INIT) == 0)
!               sv->sv_flags |= SVFLAG_ASSIGNED;
            newsav->sav_var_vals_idx = si->sn_var_vals.ga_len;
            ++si->sn_var_vals.ga_len;
            STRCPY(&newsav->sav_key, name);
***************
*** 970,976 ****
            // "var b: blob = null_blob" has a different type.
            *type = &t_blob_null;
        }
!       if (sv->sv_type_allocated)
            free_type(sv->sv_type);
        if (*type != NULL && ((*type)->tt_type == VAR_FUNC
                                           || (*type)->tt_type == VAR_PARTIAL))
--- 972,978 ----
            // "var b: blob = null_blob" has a different type.
            *type = &t_blob_null;
        }
!       if (sv->sv_flags & SVFLAG_TYPE_ALLOCATED)
            free_type(sv->sv_type);
        if (*type != NULL && ((*type)->tt_type == VAR_FUNC
                                           || (*type)->tt_type == VAR_PARTIAL))
***************
*** 979,990 ****
            // function is freed, but the script variable may keep the type.
            // Make a copy to avoid using freed memory.
            sv->sv_type = alloc_type(*type);
!           sv->sv_type_allocated = TRUE;
        }
        else
        {
            sv->sv_type = *type;
!           sv->sv_type_allocated = FALSE;
        }
      }
  
--- 981,992 ----
            // function is freed, but the script variable may keep the type.
            // Make a copy to avoid using freed memory.
            sv->sv_type = alloc_type(*type);
!           sv->sv_flags |= SVFLAG_TYPE_ALLOCATED;
        }
        else
        {
            sv->sv_type = *type;
!           sv->sv_flags &= ~SVFLAG_TYPE_ALLOCATED;
        }
      }
  
*** ../vim-8.2.4697/src/vim9execute.c   2022-04-05 17:30:23.263606787 +0100
--- src/vim9execute.c   2022-04-05 20:39:55.398249881 +0100
***************
*** 1514,1520 ****
        return NULL;
      }
  
!     if (!sv->sv_export && sref->sref_sid != current_sctx.sc_sid)
      {
        if (dfunc != NULL)
            semsg(_(e_item_not_exported_in_script_str), sv->sv_name);
--- 1514,1521 ----
        return NULL;
      }
  
!     if ((sv->sv_flags & SVFLAG_EXPORTED) == 0
!                                     && sref->sref_sid != current_sctx.sc_sid)
      {
        if (dfunc != NULL)
            semsg(_(e_item_not_exported_in_script_str), sv->sv_name);
***************
*** 2952,2958 ****
                            {
                                sv = ((svar_T *)SCRIPT_ITEM(sid)
                                                  ->sn_var_vals.ga_data) + idx;
!                               if (!sv->sv_export)
                                {
                                    SOURCING_LNUM = iptr->isn_lnum;
                                    semsg(_(e_item_not_exported_in_script_str),
--- 2953,2959 ----
                            {
                                sv = ((svar_T *)SCRIPT_ITEM(sid)
                                                  ->sn_var_vals.ga_data) + idx;
!                               if ((sv->sv_flags & SVFLAG_EXPORTED) == 0)
                                {
                                    SOURCING_LNUM = iptr->isn_lnum;
                                    semsg(_(e_item_not_exported_in_script_str),
***************
*** 3117,3123 ****
                                svar_T  *sv = ((svar_T *)SCRIPT_ITEM(sid)
                                                  ->sn_var_vals.ga_data) + idx;
  
!                               if (!sv->sv_export)
                                {
                                    semsg(_(e_item_not_exported_in_script_str),
                                                                         name);
--- 3118,3124 ----
                                svar_T  *sv = ((svar_T *)SCRIPT_ITEM(sid)
                                                  ->sn_var_vals.ga_data) + idx;
  
!                               if ((sv->sv_flags & SVFLAG_EXPORTED) == 0)
                                {
                                    semsg(_(e_item_not_exported_in_script_str),
                                                                         name);
*** ../vim-8.2.4697/src/evalvars.c      2022-04-04 14:58:02.166539812 +0100
--- src/evalvars.c      2022-04-05 20:50:30.340339928 +0100
***************
*** 2828,2840 ****
        }
        else if (rettv != NULL)
        {
            if (ht != NULL && ht == get_script_local_ht()
                    && tv != &SCRIPT_SV(current_sctx.sc_sid)->sv_var.di_tv)
            {
!               svar_T *sv = find_typval_in_script(tv, 0, TRUE);
! 
                if (sv != NULL)
                    type = sv->sv_type;
            }
  
            // If a list or dict variable wasn't initialized and has meaningful
--- 2828,2845 ----
        }
        else if (rettv != NULL)
        {
+           svar_T  *sv = NULL;
+           int     was_assigned = FALSE;
+ 
            if (ht != NULL && ht == get_script_local_ht()
                    && tv != &SCRIPT_SV(current_sctx.sc_sid)->sv_var.di_tv)
            {
!               sv = find_typval_in_script(tv, 0, TRUE);
                if (sv != NULL)
+               {
                    type = sv->sv_type;
+                   was_assigned = sv->sv_flags & SVFLAG_ASSIGNED;
+               }
            }
  
            // If a list or dict variable wasn't initialized and has meaningful
***************
*** 2843,2849 ****
            if (ht != &globvarht)
            {
                if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL
!                         && ((type != NULL && type != &t_dict_empty)
                                                          || !in_vim9script()))
                {
                    tv->vval.v_dict = dict_alloc();
--- 2848,2854 ----
            if (ht != &globvarht)
            {
                if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL
!                                           && ((type != NULL && !was_assigned)
                                                          || !in_vim9script()))
                {
                    tv->vval.v_dict = dict_alloc();
***************
*** 2851,2860 ****
                    {
                        ++tv->vval.v_dict->dv_refcount;
                        tv->vval.v_dict->dv_type = alloc_type(type);
                    }
                }
                else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL
!                                   && ((type != NULL && type != &t_list_empty)
                                                          || !in_vim9script()))
                {
                    tv->vval.v_list = list_alloc();
--- 2856,2867 ----
                    {
                        ++tv->vval.v_dict->dv_refcount;
                        tv->vval.v_dict->dv_type = alloc_type(type);
+                       if (sv != NULL)
+                           sv->sv_flags |= SVFLAG_ASSIGNED;
                    }
                }
                else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL
!                                           && ((type != NULL && !was_assigned)
                                                          || !in_vim9script()))
                {
                    tv->vval.v_list = list_alloc();
***************
*** 2862,2876 ****
                    {
                        ++tv->vval.v_list->lv_refcount;
                        tv->vval.v_list->lv_type = alloc_type(type);
                    }
                }
                else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL
!                                   && ((type != NULL && type != &t_blob_null)
                                                          || !in_vim9script()))
                {
                    tv->vval.v_blob = blob_alloc();
                    if (tv->vval.v_blob != NULL)
                        ++tv->vval.v_blob->bv_refcount;
                }
            }
            copy_tv(tv, rettv);
--- 2869,2889 ----
                    {
                        ++tv->vval.v_list->lv_refcount;
                        tv->vval.v_list->lv_type = alloc_type(type);
+                       if (sv != NULL)
+                           sv->sv_flags |= SVFLAG_ASSIGNED;
                    }
                }
                else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL
!                                           && ((type != NULL && !was_assigned)
                                                          || !in_vim9script()))
                {
                    tv->vval.v_blob = blob_alloc();
                    if (tv->vval.v_blob != NULL)
+                   {
                        ++tv->vval.v_blob->bv_refcount;
+                       if (sv != NULL)
+                           sv->sv_flags |= SVFLAG_ASSIGNED;
+                   }
                }
            }
            copy_tv(tv, rettv);
***************
*** 3587,3592 ****
--- 3600,3606 ----
                        goto failed;
                    if (type == NULL)
                        type = sv->sv_type;
+                   sv->sv_flags |= SVFLAG_ASSIGNED;
                }
            }
  
*** ../vim-8.2.4697/src/testdir/test_vim9_assign.vim    2022-04-01 
15:26:54.988558723 +0100
--- src/testdir/test_vim9_assign.vim    2022-04-05 21:38:11.555791492 +0100
***************
*** 740,745 ****
--- 740,746 ----
  enddef
  
  def Test_extend_list()
+   # using uninitilaized list assigns empty list
    var lines =<< trim END
        var l1: list<number>
        var l2 = l1
***************
*** 757,763 ****
    END
    v9.CheckDefAndScriptSuccess(lines)
  
!   # appending to NULL list from a function
    lines =<< trim END
        vim9script
        var list: list<string>
--- 758,764 ----
    END
    v9.CheckDefAndScriptSuccess(lines)
  
!   # appending to uninitialzed list from a function works
    lines =<< trim END
        vim9script
        var list: list<string>
***************
*** 779,791 ****
    END
    v9.CheckScriptSuccess(lines)
  
    lines =<< trim END
        vim9script
        var l: list<string> = test_null_list()
        extend(l, ['x'])
-       assert_equal(['x'], l)
    END
!   v9.CheckScriptSuccess(lines)
  
    lines =<< trim END
        vim9script
--- 780,809 ----
    END
    v9.CheckScriptSuccess(lines)
  
+   # initialized to null, with type, does not default to empty list
    lines =<< trim END
        vim9script
        var l: list<string> = test_null_list()
        extend(l, ['x'])
    END
!   v9.CheckScriptFailure(lines, 'E1134:', 3)
! 
!   # initialized to null, without type, does not default to empty list
!   lines =<< trim END
!       vim9script
!       var l = null_list
!       extend(l, ['x'])
!   END
!   v9.CheckScriptFailure(lines, 'E1134:', 3)
! 
!   # assigned null, does not default to empty list
!   lines =<< trim END
!       vim9script
!       var l: list<string>
!       l = null_list
!       extend(l, ['x'])
!   END
!   v9.CheckScriptFailure(lines, 'E1134:', 4)
  
    lines =<< trim END
        vim9script
***************
*** 838,846 ****
        vim9script
        var d: dict<string> = test_null_dict()
        extend(d, {a: 'x'})
-       assert_equal({a: 'x'}, d)
    END
!   v9.CheckScriptSuccess(lines)
  
    lines =<< trim END
        vim9script
--- 856,863 ----
        vim9script
        var d: dict<string> = test_null_dict()
        extend(d, {a: 'x'})
    END
!   v9.CheckScriptFailure(lines, 'E1133:', 3)
  
    lines =<< trim END
        vim9script
*** ../vim-8.2.4697/src/testdir/test_vim9_builtin.vim   2022-04-03 
21:30:25.022559205 +0100
--- src/testdir/test_vim9_builtin.vim   2022-04-05 21:20:43.395382768 +0100
***************
*** 153,166 ****
    END
    v9.CheckDefExecFailure(lines, 'E1130:', 2)
  
!   # Getting variable with NULL list allocates a new list at script level
    lines =<< trim END
        vim9script
!       var l: list<number> = test_null_list()
        add(l, 123)
    END
    v9.CheckScriptSuccess(lines)
  
    lines =<< trim END
        vim9script
        var l: list<string> = ['a']
--- 153,174 ----
    END
    v9.CheckDefExecFailure(lines, 'E1130:', 2)
  
!   # Getting an uninitialized variable allocates a new list at script level
    lines =<< trim END
        vim9script
!       var l: list<number>
        add(l, 123)
    END
    v9.CheckScriptSuccess(lines)
  
+   # Adding to a variable set to a NULL list fails
+   lines =<< trim END
+       vim9script
+       var l: list<number> = test_null_list()
+       add(l, 123)
+   END
+   v9.CheckScriptFailure(lines, 'E1130:', 3)
+ 
    lines =<< trim END
        vim9script
        var l: list<string> = ['a']
*** ../vim-8.2.4697/src/version.c       2022-04-05 17:30:23.263606787 +0100
--- src/version.c       2022-04-05 21:39:02.175669700 +0100
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     4698,
  /**/

-- 
So when I saw the post to comp.editors, I rushed over to the FTP site to
grab it.  So I yank apart the tarball, light x candles, where x= the
vim version multiplied by the md5sum of the source divided by the MAC of
my NIC (8A3FA78155A8A1D346C3C4A), put on black robes, dim the lights,
wave a dead chicken over the hard drive, and summon the power of GNU GCC
with the magic words "make config ; make!".
                [Jason Spence, compiling Vim 5.0]

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220405204107.C7EB41C0561%40moolenaar.net.

Raspunde prin e-mail lui