Skip to content

Commit 24e9316

Browse files
committed
patch 8.2.3179: Vim9: cannot assign to an imported variable at script level
Problem: Vim9: cannot assign to an imported variable at script level. Solution: Lookup imported items when assigning.
1 parent 4db572e commit 24e9316

5 files changed

Lines changed: 223 additions & 157 deletions

File tree

src/errors.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,5 @@ EXTERN char e_list_required_for_argument_nr[]
506506
INIT(= N_("E1211: List required for argument %d"));
507507
EXTERN char e_bool_required_for_argument_nr[]
508508
INIT(= N_("E1211: Bool required for argument %d"));
509+
EXTERN char e_redefining_imported_item_str[]
510+
INIT(= N_("E1212: Redefining imported item %s"));

src/eval.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1358,7 +1358,8 @@ set_var_lval(
13581358
|| (!var_check_ro(di->di_flags, lp->ll_name, FALSE)
13591359
&& !tv_check_lock(&di->di_tv, lp->ll_name, FALSE)))
13601360
&& tv_op(&tv, rettv, op) == OK)
1361-
set_var(lp->ll_name, &tv, FALSE);
1361+
set_var_const(lp->ll_name, NULL, &tv, FALSE,
1362+
ASSIGN_NO_DECL, 0);
13621363
clear_tv(&tv);
13631364
}
13641365
}

src/evalvars.c

Lines changed: 162 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -3201,6 +3201,7 @@ set_var_const(
32013201
typval_T *tv = tv_arg;
32023202
typval_T bool_tv;
32033203
dictitem_T *di;
3204+
typval_T *dest_tv = NULL;
32043205
char_u *varname;
32053206
hashtab_T *ht;
32063207
int is_script_local;
@@ -3241,182 +3242,210 @@ set_var_const(
32413242

32423243
di = find_var_in_ht(ht, 0, varname, TRUE);
32433244

3244-
// Search in parent scope which is possible to reference from lambda
3245-
if (di == NULL)
3246-
di = find_var_in_scoped_ht(name, TRUE);
3247-
3248-
if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
3249-
&& var_wrong_func_name(name, di == NULL))
3250-
goto failed;
3251-
3252-
if (need_convert_to_bool(type, tv))
3245+
if (di == NULL && var_in_vim9script)
32533246
{
3254-
// Destination is a bool and the value is not, but it can be converted.
3255-
CLEAR_FIELD(bool_tv);
3256-
bool_tv.v_type = VAR_BOOL;
3257-
bool_tv.vval.v_number = tv2bool(tv) ? VVAL_TRUE : VVAL_FALSE;
3258-
tv = &bool_tv;
3259-
}
3247+
imported_T *import = find_imported(varname, 0, NULL);
32603248

3261-
if (di != NULL)
3262-
{
3263-
// Item already exists. Allowed to replace when reloading.
3264-
if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
3249+
if (import != NULL)
32653250
{
3266-
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
3267-
&& (flags & ASSIGN_FOR_LOOP) == 0)
3268-
{
3269-
emsg(_(e_cannot_mod));
3270-
goto failed;
3271-
}
3251+
scriptitem_T *si = SCRIPT_ITEM(import->imp_sid);
3252+
svar_T *sv;
32723253

3273-
if (is_script_local && vim9script
3274-
&& (flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0)
3254+
// imported variable from another script
3255+
if ((flags & ASSIGN_NO_DECL) == 0)
32753256
{
3276-
semsg(_(e_redefining_script_item_str), name);
3257+
semsg(_(e_redefining_imported_item_str), name);
32773258
goto failed;
32783259
}
3260+
sv = ((svar_T *)si->sn_var_vals.ga_data)
3261+
+ import->imp_var_vals_idx;
3262+
// TODO: check the type
3263+
// TODO: check for const and locked
3264+
dest_tv = sv->sv_tv;
3265+
}
3266+
}
32793267

3280-
if (var_in_vim9script)
3281-
{
3282-
where_T where;
3268+
if (dest_tv == NULL)
3269+
{
3270+
// Search in parent scope which is possible to reference from lambda
3271+
if (di == NULL)
3272+
di = find_var_in_scoped_ht(name, TRUE);
32833273

3284-
// check the type and adjust to bool if needed
3285-
where.wt_index = var_idx;
3286-
where.wt_variable = TRUE;
3287-
if (check_script_var_type(&di->di_tv, tv, name, where) == FAIL)
3288-
goto failed;
3289-
}
3274+
if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
3275+
&& var_wrong_func_name(name, di == NULL))
3276+
goto failed;
32903277

3291-
if (var_check_permission(di, name) == FAIL)
3292-
goto failed;
3293-
}
3294-
else
3278+
if (need_convert_to_bool(type, tv))
32953279
{
3296-
// can only redefine once
3297-
di->di_flags &= ~DI_FLAGS_RELOAD;
3298-
3299-
// A Vim9 script-local variable is also present in sn_all_vars and
3300-
// sn_var_vals. It may set "type" from "tv".
3301-
if (var_in_vim9script)
3302-
update_vim9_script_var(FALSE, di, flags, tv, &type,
3303-
(flags & ASSIGN_NO_MEMBER_TYPE) == 0);
3280+
// Destination is a bool and the value is not, but it can be converted.
3281+
CLEAR_FIELD(bool_tv);
3282+
bool_tv.v_type = VAR_BOOL;
3283+
bool_tv.vval.v_number = tv2bool(tv) ? VVAL_TRUE : VVAL_FALSE;
3284+
tv = &bool_tv;
33043285
}
33053286

3306-
// existing variable, need to clear the value
3307-
3308-
// Handle setting internal di: variables separately where needed to
3309-
// prevent changing the type.
3310-
if (ht == &vimvarht)
3287+
if (di != NULL)
33113288
{
3312-
if (di->di_tv.v_type == VAR_STRING)
3289+
// Item already exists. Allowed to replace when reloading.
3290+
if ((di->di_flags & DI_FLAGS_RELOAD) == 0)
33133291
{
3314-
VIM_CLEAR(di->di_tv.vval.v_string);
3315-
if (copy || tv->v_type != VAR_STRING)
3292+
if ((flags & (ASSIGN_CONST | ASSIGN_FINAL))
3293+
&& (flags & ASSIGN_FOR_LOOP) == 0)
33163294
{
3317-
char_u *val = tv_get_string(tv);
3295+
emsg(_(e_cannot_mod));
3296+
goto failed;
3297+
}
33183298

3319-
// Careful: when assigning to v:errmsg and tv_get_string()
3320-
// causes an error message the variable will already be set.
3321-
if (di->di_tv.vval.v_string == NULL)
3322-
di->di_tv.vval.v_string = vim_strsave(val);
3299+
if (is_script_local && vim9script
3300+
&& (flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0)
3301+
{
3302+
semsg(_(e_redefining_script_item_str), name);
3303+
goto failed;
33233304
}
3324-
else
3305+
3306+
if (var_in_vim9script)
33253307
{
3326-
// Take over the string to avoid an extra alloc/free.
3327-
di->di_tv.vval.v_string = tv->vval.v_string;
3328-
tv->vval.v_string = NULL;
3308+
where_T where;
3309+
3310+
// check the type and adjust to bool if needed
3311+
where.wt_index = var_idx;
3312+
where.wt_variable = TRUE;
3313+
if (check_script_var_type(&di->di_tv, tv, name, where) == FAIL)
3314+
goto failed;
33293315
}
3330-
goto failed;
3316+
3317+
if (var_check_permission(di, name) == FAIL)
3318+
goto failed;
33313319
}
3332-
else if (di->di_tv.v_type == VAR_NUMBER)
3320+
else
33333321
{
3334-
di->di_tv.vval.v_number = tv_get_number(tv);
3335-
if (STRCMP(varname, "searchforward") == 0)
3336-
set_search_direction(di->di_tv.vval.v_number ? '/' : '?');
3337-
#ifdef FEAT_SEARCH_EXTRA
3338-
else if (STRCMP(varname, "hlsearch") == 0)
3322+
// can only redefine once
3323+
di->di_flags &= ~DI_FLAGS_RELOAD;
3324+
3325+
// A Vim9 script-local variable is also present in sn_all_vars and
3326+
// sn_var_vals. It may set "type" from "tv".
3327+
if (var_in_vim9script)
3328+
update_vim9_script_var(FALSE, di, flags, tv, &type,
3329+
(flags & ASSIGN_NO_MEMBER_TYPE) == 0);
3330+
}
3331+
3332+
// existing variable, need to clear the value
3333+
3334+
// Handle setting internal di: variables separately where needed to
3335+
// prevent changing the type.
3336+
if (ht == &vimvarht)
3337+
{
3338+
if (di->di_tv.v_type == VAR_STRING)
33393339
{
3340-
no_hlsearch = !di->di_tv.vval.v_number;
3341-
redraw_all_later(SOME_VALID);
3340+
VIM_CLEAR(di->di_tv.vval.v_string);
3341+
if (copy || tv->v_type != VAR_STRING)
3342+
{
3343+
char_u *val = tv_get_string(tv);
3344+
3345+
// Careful: when assigning to v:errmsg and tv_get_string()
3346+
// causes an error message the variable will already be set.
3347+
if (di->di_tv.vval.v_string == NULL)
3348+
di->di_tv.vval.v_string = vim_strsave(val);
3349+
}
3350+
else
3351+
{
3352+
// Take over the string to avoid an extra alloc/free.
3353+
di->di_tv.vval.v_string = tv->vval.v_string;
3354+
tv->vval.v_string = NULL;
3355+
}
3356+
goto failed;
33423357
}
3358+
else if (di->di_tv.v_type == VAR_NUMBER)
3359+
{
3360+
di->di_tv.vval.v_number = tv_get_number(tv);
3361+
if (STRCMP(varname, "searchforward") == 0)
3362+
set_search_direction(di->di_tv.vval.v_number ? '/' : '?');
3363+
#ifdef FEAT_SEARCH_EXTRA
3364+
else if (STRCMP(varname, "hlsearch") == 0)
3365+
{
3366+
no_hlsearch = !di->di_tv.vval.v_number;
3367+
redraw_all_later(SOME_VALID);
3368+
}
33433369
#endif
3344-
goto failed;
3370+
goto failed;
3371+
}
3372+
else if (di->di_tv.v_type != tv->v_type)
3373+
{
3374+
semsg(_("E963: setting %s to value with wrong type"), name);
3375+
goto failed;
3376+
}
33453377
}
3346-
else if (di->di_tv.v_type != tv->v_type)
3378+
3379+
clear_tv(&di->di_tv);
3380+
}
3381+
else
3382+
{
3383+
// Item not found, check if a function already exists.
3384+
if (is_script_local && (flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0
3385+
&& lookup_scriptitem(name, STRLEN(name), FALSE, NULL) == OK)
33473386
{
3348-
semsg(_("E963: setting %s to value with wrong type"), name);
3387+
semsg(_(e_redefining_script_item_str), name);
33493388
goto failed;
33503389
}
3351-
}
33523390

3353-
clear_tv(&di->di_tv);
3354-
}
3355-
else
3356-
{
3357-
// Item not found, check if a function already exists.
3358-
if (is_script_local && (flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0
3359-
&& lookup_scriptitem(name, STRLEN(name), FALSE, NULL) == OK)
3360-
{
3361-
semsg(_(e_redefining_script_item_str), name);
3362-
goto failed;
3363-
}
3391+
// add a new variable
3392+
if (var_in_vim9script && (flags & ASSIGN_NO_DECL))
3393+
{
3394+
semsg(_(e_unknown_variable_str), name);
3395+
goto failed;
3396+
}
33643397

3365-
// add a new variable
3366-
if (var_in_vim9script && (flags & ASSIGN_NO_DECL))
3367-
{
3368-
semsg(_(e_unknown_variable_str), name);
3369-
goto failed;
3370-
}
3398+
// Can't add "v:" or "a:" variable.
3399+
if (ht == &vimvarht || ht == get_funccal_args_ht())
3400+
{
3401+
semsg(_(e_illvar), name);
3402+
goto failed;
3403+
}
33713404

3372-
// Can't add "v:" or "a:" variable.
3373-
if (ht == &vimvarht || ht == get_funccal_args_ht())
3374-
{
3375-
semsg(_(e_illvar), name);
3376-
goto failed;
3377-
}
3405+
// Make sure the variable name is valid. In Vim9 script an autoload
3406+
// variable must be prefixed with "g:".
3407+
if (!valid_varname(varname, !vim9script
3408+
|| STRNCMP(name, "g:", 2) == 0))
3409+
goto failed;
33783410

3379-
// Make sure the variable name is valid. In Vim9 script an autoload
3380-
// variable must be prefixed with "g:".
3381-
if (!valid_varname(varname, !vim9script
3382-
|| STRNCMP(name, "g:", 2) == 0))
3383-
goto failed;
3411+
di = alloc(sizeof(dictitem_T) + STRLEN(varname));
3412+
if (di == NULL)
3413+
goto failed;
3414+
STRCPY(di->di_key, varname);
3415+
if (hash_add(ht, DI2HIKEY(di)) == FAIL)
3416+
{
3417+
vim_free(di);
3418+
goto failed;
3419+
}
3420+
di->di_flags = DI_FLAGS_ALLOC;
3421+
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
3422+
di->di_flags |= DI_FLAGS_LOCK;
33843423

3385-
di = alloc(sizeof(dictitem_T) + STRLEN(varname));
3386-
if (di == NULL)
3387-
goto failed;
3388-
STRCPY(di->di_key, varname);
3389-
if (hash_add(ht, DI2HIKEY(di)) == FAIL)
3390-
{
3391-
vim_free(di);
3392-
goto failed;
3424+
// A Vim9 script-local variable is also added to sn_all_vars and
3425+
// sn_var_vals. It may set "type" from "tv".
3426+
if (var_in_vim9script)
3427+
update_vim9_script_var(TRUE, di, flags, tv, &type,
3428+
(flags & ASSIGN_NO_MEMBER_TYPE) == 0);
33933429
}
3394-
di->di_flags = DI_FLAGS_ALLOC;
3395-
if (flags & (ASSIGN_CONST | ASSIGN_FINAL))
3396-
di->di_flags |= DI_FLAGS_LOCK;
3397-
3398-
// A Vim9 script-local variable is also added to sn_all_vars and
3399-
// sn_var_vals. It may set "type" from "tv".
3400-
if (var_in_vim9script)
3401-
update_vim9_script_var(TRUE, di, flags, tv, &type,
3402-
(flags & ASSIGN_NO_MEMBER_TYPE) == 0);
3430+
3431+
dest_tv = &di->di_tv;
34033432
}
34043433

34053434
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
3406-
copy_tv(tv, &di->di_tv);
3435+
copy_tv(tv, dest_tv);
34073436
else
34083437
{
3409-
di->di_tv = *tv;
3410-
di->di_tv.v_lock = 0;
3438+
*dest_tv = *tv;
3439+
dest_tv->v_lock = 0;
34113440
init_tv(tv);
34123441
}
34133442

34143443
if (vim9script && type != NULL)
34153444
{
3416-
if (type->tt_type == VAR_DICT && di->di_tv.vval.v_dict != NULL)
3417-
di->di_tv.vval.v_dict->dv_type = alloc_type(type);
3418-
else if (type->tt_type == VAR_LIST && di->di_tv.vval.v_list != NULL)
3419-
di->di_tv.vval.v_list->lv_type = alloc_type(type);
3445+
if (type->tt_type == VAR_DICT && dest_tv->vval.v_dict != NULL)
3446+
dest_tv->vval.v_dict->dv_type = alloc_type(type);
3447+
else if (type->tt_type == VAR_LIST && dest_tv->vval.v_list != NULL)
3448+
dest_tv->vval.v_list->lv_type = alloc_type(type);
34203449
}
34213450

34223451
// ":const var = value" locks the value
@@ -3425,8 +3454,9 @@ set_var_const(
34253454
// Like :lockvar! name: lock the value and what it contains, but only
34263455
// if the reference count is up to one. That locks only literal
34273456
// values.
3428-
item_lock(&di->di_tv, DICT_MAXNEST, TRUE, TRUE);
3457+
item_lock(dest_tv, DICT_MAXNEST, TRUE, TRUE);
34293458
return;
3459+
34303460
failed:
34313461
if (!copy)
34323462
clear_tv(tv_arg);

0 commit comments

Comments
 (0)