To: vim_dev@googlegroups.com Subject: Patch 8.2.4698 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 var l2 = l1 *************** *** 757,763 **** END v9.CheckDefAndScriptSuccess(lines) ! # appending to NULL list from a function lines =<< trim END vim9script var list: list --- 758,764 ---- END v9.CheckDefAndScriptSuccess(lines) ! # appending to uninitialzed list from a function works lines =<< trim END vim9script var list: list *************** *** 779,791 **** END v9.CheckScriptSuccess(lines) lines =<< trim END vim9script var l: list = 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 = 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 ! l = null_list ! extend(l, ['x']) ! END ! v9.CheckScriptFailure(lines, 'E1134:', 4) lines =<< trim END vim9script *************** *** 838,846 **** vim9script var d: dict = 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 = 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 = test_null_list() add(l, 123) END v9.CheckScriptSuccess(lines) lines =<< trim END vim9script var l: list = ['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 add(l, 123) END v9.CheckScriptSuccess(lines) + # Adding to a variable set to a NULL list fails + lines =<< trim END + vim9script + var l: list = test_null_list() + add(l, 123) + END + v9.CheckScriptFailure(lines, 'E1130:', 3) + lines =<< trim END vim9script var l: list = ['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 -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///