patch 9.2.1130: Vim9: assigning a Number to $VAR or @r is not an error

Problem:  Vim9: assigning a non-String value to an environment
          variable, a register or a String v: variable at the script
          level converts the value, while a :def function gives E1012.
Solution: Also check the type at the script level and give E1012

Using "@#" still takes a Number with "=".

Add Test_assign_string_only(), which checks that these fail with E1012
both in a :def function and at the script level: "= 123", "..= 123"
and "= true" on $XSTRING_ONLY, @a and v:errmsg, "@# ..= 1" and
"[$XSTRING_ONLY, @a] = [1, 2]".  It also checks that legacy :let still
converts a Number to a String for these three targets.

closes: #21364

Assisted-by: Claude
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Hirohito Higashi authored and Christian Brabandt committed 2026-09-25 20:49:31 +00:00
1 parent 7c16e2127f
commit 12ff5ce7fd
5 files changed
+72 -1

No files matched your search

+7 -1
View File
@@ -1,4 +1,4 @@
*vim9.txt* For Vim version 9.2. Last change: 2026 Sep 24
*vim9.txt* For Vim version 9.2. Last change: 2026 Sep 25
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -4620,6 +4620,12 @@ in Vim9 script.
echo filter([1, 2], 0)
vim9cmd echo filter([1, 2], 0) # E1024: Using a Number as a String
<
- Assigning a number to an environment variable, a register or a String
v: variable:
>vim
let $FOO = 123
vim9cmd $FOO = 123 # E1012: Type mismatch; expected string but ...
<
- Not using a number where a number is expected:
>vim
" In this example, legacy Vim script treats v:false as 0
+14
View File
@@ -2417,6 +2417,20 @@ set_var_lval(
return;
}
// In Vim9 script a String v: variable only takes a String.
if (in_vim9script() && STRNCMP(lp->ll_name, "v:", 2) == 0
&& tv.v_type == VAR_STRING)
{
where_T where = WHERE_INIT;
if (check_typval_type(&t_string, rettv, where) == FAIL)
{
clear_tv(&tv);
*endp = cc;
return;
}
}
if ((di == NULL
|| (!var_check_ro(di->di_flags, lp->ll_name, FALSE)
&& !tv_check_lock(&di->di_tv, lp->ll_name, FALSE)))
+22
View File
@@ -1991,6 +1991,18 @@ ex_let_one(
if (check_typval_is_value(tv) == FAIL)
return NULL;
// In Vim9 script an environment variable and a register only take a
// String. "@#" also takes a buffer number.
if (in_vim9script() && (*arg == '$' || *arg == '@')
&& !(*arg == '@' && arg[1] == '#' && tv->v_type == VAR_NUMBER
&& (op == NULL || *op == '=')))
{
where_T where = WHERE_INIT;
if (check_typval_type(&t_string, tv, where) == FAIL)
return NULL;
}
if (*arg == '$')
{
// ":let $VAR = expr": Set environment variable.
@@ -4327,6 +4339,16 @@ set_var_const(
// existing variable, need to clear the value
// In Vim9 script a String v: variable only takes a String.
if (ht == &vimvarht && di->di_tv.v_type == VAR_STRING
&& in_vim9script())
{
where_T where = WHERE_INIT;
if (check_typval_type(&t_string, tv, where) == FAIL)
goto failed;
}
// Handle setting internal v: variables separately where needed to
// prevent changing the type.
int type_error = FALSE;
+27
View File
@@ -308,6 +308,33 @@ def Test_assign_register()
v9.CheckDefFailure(['@a += 123'], 'E1012:')
enddef
" An environment variable, a register and a String v: variable only take a
" String.
def Test_assign_string_only()
for target in ['$XSTRING_ONLY', '@a', 'v:errmsg']
v9.CheckDefAndScriptFailure([target .. ' = 123'],
'E1012: Type mismatch; expected string but got number', 1)
v9.CheckDefAndScriptFailure([target .. ' ..= 123'],
'E1012: Type mismatch; expected string but got number', 1)
v9.CheckDefAndScriptFailure([target .. ' = true'],
'E1012: Type mismatch; expected string but got bool', 1)
endfor
v9.CheckDefAndScriptFailure(['[$XSTRING_ONLY, @a] = [1, 2]'],
'E1012: Type mismatch; expected string but got number', 1)
v9.CheckDefAndScriptFailure(['@# ..= 1'],
'E1012: Type mismatch; expected string but got number', 1)
v9.CheckDefAndScriptSuccess(['@# = bufnr()'])
# Legacy script converts the value.
legacy let $XSTRING_ONLY = 123
legacy let @a = 456
legacy let v:errmsg = 789
assert_equal('123', $XSTRING_ONLY)
assert_equal('456', @a)
assert_equal('789', v:errmsg)
unlet $XSTRING_ONLY
enddef
def Test_reserved_name()
var more_names = ['null_job', 'null_channel']
if !has('job')
+2
View File
@@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1130,
/**/
1129,
/**/