patch 9.1.2106: Vim9: class, enum and type alias can be used as value
Commit:
https://github.com/vim/vim/commit/7d22f84f0b8107fb2eb7edb5665f08daa5dae12a
Author: Yegappan Lakshmanan <
yega...@yahoo.com>
Date: Fri Jan 23 19:17:29 2026 +0000
patch 9.1.2106: Vim9: class, enum and type alias can be used as value
Problem: Vim9: class, enum and type alias can be used as value in an
expression (kennypete)
Solution: Abort expression evaluation if class, enum or type alias is
used in an expression (Yegappan Lakshmanan)
related: #19173
closes: #19238
Signed-off-by: Yegappan Lakshmanan <
yega...@yahoo.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/eval.c b/src/eval.c
index 4a9f5821b..9678ea7f6 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -3646,7 +3646,15 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
int error = FALSE;
if (op_falsy)
+ {
+ // Is this typeval supported with the falsy operator?
+ if (check_typval_is_value(rettv) == FAIL)
+ {
+ clear_tv(rettv);
+ return FAIL;
+ }
result = tv2bool(rettv);
+ }
else if (vim9script)
result = tv_get_bool_chk(rettv, &error);
else if (tv_get_number_chk(rettv, &error) != 0)
@@ -5376,7 +5384,15 @@ eval9_leader(
while (VIM_ISWHITE(end_leader[-1]))
--end_leader;
if (vim9script && end_leader[-1] == '!')
+ {
+ // Is this typeval supported with the ! operator?
+ if (check_typval_is_value(rettv) == FAIL)
+ {
+ clear_tv(rettv);
+ return FAIL;
+ }
val = tv2bool(rettv);
+ }
else
val = tv_get_number_chk(rettv, &error);
}
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index b66372884..4551ea5eb 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -280,6 +280,69 @@ def Test_expr1_falsy()
END
v9.CheckSourceScriptFailure(lines, 'E1405: Class "B" cannot be used as a value')
+ # Expression evaluation should stop after using a class with the falsy
+ # operator
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ var output: string = 'pass'
+ try
+ echo C ?? 'falsy' !! execute("output = 'fail'")
+ catch /E1405:/
+ endtry
+ assert_equal('pass', output)
+ END
+ v9.CheckSourceScriptSuccess(lines)
+
+ # When using a class with the falsy operator, expression evaluation should
+ # be aborted in a function
+ g:falsy_output = 'pass'
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ g:falsy_output = 'pass'
+ def Fn()
+ echo C ?? 'falsy' !! execute('g:falsy_output = "fail"')
+ enddef
+ Fn()
+ END
+ v9.CheckSourceScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
+ assert_equal('pass', g:falsy_output)
+ unlet g:falsy_output
+
+ # When using a class with the "!" operator, expression evaluation should be
+ # aborted
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ var output: string = 'pass'
+ try
+ echo !C !! execute("output = 'fail'")
+ catch /E1405:/
+ endtry
+ assert_equal('pass', output)
+ END
+ v9.CheckSourceScriptSuccess(lines)
+
+ # When using a class with the "!" operator, expression evaluation should be
+ # aborted in a function
+ g:falsy_output = 'pass'
+ lines =<< trim END
+ vim9script
+ class C
+ endclass
+ def Fn()
+ echo !C !! execute('g:falsy_output = "fail"')
+ enddef
+ Fn()
+ END
+ v9.CheckSourceScriptFailure(lines, 'E1405: Class "C" cannot be used as a value', 1)
+ assert_equal('pass', g:falsy_output)
+ unlet g:falsy_output
+
lines =<< trim END
vim9script
echo null_class ?? 'falsy'
@@ -309,6 +372,68 @@ def Test_expr1_falsy()
END
v9.CheckSourceScriptFailure(lines, 'E1421: Enum "E2" cannot be used as a value')
+ # Expression evaluation should stop after using an enum with the falsy
+ # operator
+ lines =<< trim END
+ vim9script
+ enum E
+ endenum
+ var output: string = 'pass'
+ try
+ echo E ?? 'falsy' !! execute("output = 'fail'")
+ catch /E1421:/
+ endtry
+ assert_equal('pass', output)
+ END
+ v9.CheckSourceScriptSuccess(lines)
+
+ # When using a enum with the falsy operator, expression evaluation should
+ # be aborted in a function
+ g:falsy_output = 'pass'
+ lines =<< trim END
+ vim9script
+ enum E3
+ endenum
+ g:falsy_output = 'pass'
+ def Fn()
+ echo E3 ?? 'falsy' !! execute('g:falsy_output = "fail"')
+ enddef
+ Fn()
+ END
+ v9.CheckSourceScriptFailure(lines, 'E1421: Enum "E3" cannot be used as a value', 1)
+ assert_equal('pass', g:falsy_output)
+ unlet g:falsy_output
+
+ # Expression evaluation should stop after using an enum with the ! operator
+ lines =<< trim END
+ vim9script
+ enum E
+ endenum
+ var output: string = 'pass'
+ try
+ echo !E !! execute("output = 'fail'")
+ catch /E1421:/
+ endtry
+ assert_equal('pass', output)
+ END
+ v9.CheckSourceScriptSuccess(lines)
+
+ # When using a enum with the "!" operator, expression evaluation should be
+ # aborted in a function
+ g:falsy_output = 'pass'
+ lines =<< trim END
+ vim9script
+ enum E4
+ endenum
+ def Fn()
+ echo !E4 !! execute('g:falsy_output = "fail"')
+ enddef
+ Fn()
+ END
+ v9.CheckSourceScriptFailure(lines, 'E1421: Enum "E4" cannot be used as a value', 1)
+ assert_equal('pass', g:falsy_output)
+ unlet g:falsy_output
+
# typealias cannot be used with the falsy operator
lines =<< trim END
vim9script
@@ -324,6 +449,65 @@ def Test_expr1_falsy()
END
v9.CheckSourceScriptFailure(lines, 'E1403: Type alias "T2" cannot be used as a value')
+ # Expression evaluation should stop after using a typealias with the falsy
+ # operator
+ lines =<< trim END
+ vim9script
+ type T3 = dict<string>
+ var output: string = 'pass'
+ try
+ echo T3 ?? 'falsy' !! execute("output = 'fail'")
+ catch /E1403:/
+ endtry
+ assert_equal('pass', output)
+ END
+ v9.CheckSourceScriptSuccess(lines)
+
+ # When using a typealias with the falsy operator, expression evaluation
+ # should be aborted in a function
+ g:falsy_output = 'pass'
+ lines =<< trim END
+ vim9script
+ type T3 = dict<job>
+ g:falsy_output = 'pass'
+ def Fn()
+ echo T3 ?? 'falsy' !! execute('g:falsy_output = "fail"')
+ enddef
+ Fn()
+ END
+ v9.CheckSourceScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
+ assert_equal('pass', g:falsy_output)
+ unlet g:falsy_output
+
+ # Expression evaluation should stop after using a typealias with the !
+ # operator
+ lines =<< trim END
+ vim9script
+ type T3 = dict<string>
+ var output: string = 'pass'
+ try
+ echo !T3 !! execute("output = 'fail'")
+ catch /E1403:/
+ endtry
+ assert_equal('pass', output)
+ END
+ v9.CheckSourceScriptSuccess(lines)
+
+ # When using a typealias with the "!" operator, expression evaluation should
+ # be aborted in a function
+ g:falsy_output = 'pass'
+ lines =<< trim END
+ vim9script
+ type T4 = list<number>
+ def Fn()
+ echo !T4 !! execute('g:falsy_output = "fail"')
+ enddef
+ Fn()
+ END
+ v9.CheckSourceScriptFailure(lines, 'E1407: Cannot use a Typealias as a variable or value', 1)
+ assert_equal('pass', g:falsy_output)
+ unlet g:falsy_output
+
var msg = "White space required before and after '??'"
call v9.CheckDefAndScriptFailure(["var x = 1?? 'one' : 'two'"], msg, 1)
call v9.CheckDefAndScriptFailure(["var x = 1 ??'one' : 'two'"], msg, 1)
diff --git a/src/version.c b/src/version.c
index 1852977ac..5017e1d7b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 2106,
/**/
2105,
/**/
diff --git a/src/vim9expr.c b/src/vim9expr.c
index c8353ab98..ebab9ec13 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -2430,6 +2430,8 @@ compile_leader(cctx_T *cctx, int numeric_only, char_u *start, char_u **end)
invert = !invert;
--p;
}
+ if (check_type_is_value(get_type_on_stack(cctx, 0)) == FAIL)
+ return FAIL;
if (generate_2BOOL(cctx, invert, -1) == FAIL)
return FAIL;
}
@@ -3948,7 +3950,11 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
generate_JUMP(cctx, op_falsy
? JUMP_AND_KEEP_IF_TRUE : JUMP_IF_FALSE, 0);
if (op_falsy)
+ {
type1 = get_type_on_stack(cctx, -1);
+ if (check_type_is_value(type1) == FAIL)
+ return FAIL;
+ }
}
// evaluate the second expression; any type is accepted