Commit: patch 9.2.0292: E340 internal error when using method call on void value

0 views
Skip to first unread message

Christian Brabandt

unread,
Apr 4, 2026, 4:32:23 AM (4 days ago) Apr 4
to vim...@googlegroups.com
patch 9.2.0292: E340 internal error when using method call on void value

Commit: https://github.com/vim/vim/commit/a9d01da6616a702ec093b0abc75e097368889524
Author: Hirohito Higashi <h.eas...@gmail.com>
Date: Sat Apr 4 08:27:46 2026 +0000

patch 9.2.0292: E340 internal error when using method call on void value

Problem: E340 internal error when using method call on void value
(Peter Kenny)
Solution: Check for void value (Hirohito Higashi)

Using a method call on a void return value (e.g. "echo F()->empty()"
where F() returns void) caused an internal error E340. Now it properly
reports E1031 or E1186 depending on the context.

Changes:
- eval.c: check for void value before -> method call at runtime
- vim9expr.c: check for void type before -> method call at compile time
- vim9execute.c: check for void value in builtin function arguments and in
ISN_STORE

fixes: #19897
closes: #19912

Signed-off-by: Hirohito Higashi <h.eas...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/eval.c b/src/eval.c
index e8b9cc830..6f7c9960a 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -7566,7 +7566,13 @@ handle_subscript(
*arg = skipwhite(p + 2);
else
*arg = p + 2;
- if (VIM_ISWHITE(**arg))
+ if (ret == OK && evaluate && rettv->v_type == VAR_VOID)
+ {
+ if (verbose)
+ emsg(_(e_cannot_use_void_value));
+ ret = FAIL;
+ }
+ else if (VIM_ISWHITE(**arg))
{
emsg(_(e_no_white_space_allowed_before_parenthesis));
ret = FAIL;
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 4cc565628..d8f69eff3 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -402,7 +402,7 @@ enddef

def Test_bufload()
assert_fails('bufload([])', 'E1220:')
- bufload('')->assert_equal(0)
+ bufload('')
enddef

def Test_bufloaded()
@@ -647,7 +647,7 @@ def Test_ch_logfile()
else
assert_fails('ch_logfile(true)', 'E1174:')
assert_fails('ch_logfile("foo", true)', 'E1174:')
- ch_logfile('', '')->assert_equal(0)
+ ch_logfile('', '')

v9.CheckSourceDefAndScriptFailure(['ch_logfile(1)'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1'])
v9.CheckSourceDefAndScriptFailure(['ch_logfile("a", true)'], ['E1013: Argument 2: type mismatch, expected string but got bool', 'E1174: String required for argument 2'])
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index ab0b02a6d..bd0dca335 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -3996,7 +3996,7 @@ def Test_expr9_method_call()
enddef
RetVoid()->byteidx(3)
END
- v9.CheckDefExecFailure(lines, 'E1013:')
+ v9.CheckDefExecFailure(lines, 'E1031: Cannot use void value')

lines =<< trim END
const SetList = [function('len')]
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 343c2f777..f4cd1edaa 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -4864,4 +4864,144 @@ if has('perl')
endif


+def Test_void_method_chain()
+ #### Case 1: Echo, method chain source is void ####
+ # outside def: runtime error
+ var lines =<< trim END
+ vim9script
+ var Fn1a: func = (): void => {
+ }
+ echo Fn1a()->empty()
+ END
+ v9.CheckScriptFailure(lines, 'E1031: Cannot use void value')
+
+ # inside def, compile-time error (known void return)
+ lines =<< trim END
+ vim9script
+ def Fn1b(): void
+ enddef
+ def TestFunc()
+ echo Fn1b()->empty()
+ enddef
+ defcompile TestFunc
+ END
+ v9.CheckScriptFailure(lines, 'E1031: Cannot use void value')
+
+ # inside def, runtime error (untyped func)
+ lines =<< trim END
+ vim9script
+ def TestFunc()
+ var Fn1c: func = (): void => {
+ }
+ echo Fn1c()->empty()
+ enddef
+ TestFunc()
+ END
+ v9.CheckScriptFailure(lines, 'E1031: Cannot use void value')
+
+ # inside def, compile-time error (func(): void)
+ lines =<< trim END
+ vim9script
+ def TestFunc()
+ var Fn1d: func(): void = () => {
+ }
+ echo Fn1d()->empty()
+ enddef
+ defcompile TestFunc
+ END
+ v9.CheckScriptFailure(lines, 'E1031: Cannot use void value')
+
+ #### Case 2: Echo, method chain destination is void ####
+ # outside def: runtime error
+ lines =<< trim END
+ vim9script
+ var Fn2a: func = (s: string): void => {
+ }
+ echo "x"->Fn2a()
+ END
+ v9.CheckScriptFailure(lines, 'E1186: Expression does not result in a value: "x"->Fn2a()')
+
+ # inside def, compile-time error (known void return)
+ lines =<< trim END
+ vim9script
+ def Fn2b(s: string): void
+ enddef
+ def TestFunc()
+ echo "x"->Fn2b()
+ enddef
+ defcompile TestFunc
+ END
+ v9.CheckScriptFailure(lines, 'E1186: Expression does not result in a value: "x"->Fn2b()')
+
+ # inside def, runtime error (untyped func)
+ lines =<< trim END
+ vim9script
+ def TestFunc()
+ var Fn2c: func = (s: string): void => {
+ }
+ echo "x"->Fn2c()
+ enddef
+ TestFunc()
+ END
+ v9.CheckScriptFailure(lines, 'E1031: Cannot use void value')
+
+ # inside def, compile-time error (func(string): void)
+ lines =<< trim END
+ vim9script
+ def TestFunc()
+ var Fn2d: func(string): void = (s: string): void => {
+ }
+ echo "x"->Fn2d()
+ enddef
+ defcompile TestFunc
+ END
+ v9.CheckScriptFailure(lines, 'E1186: Expression does not result in a value: "x"->Fn2d()')
+
+ #### Case 3: Assignment, RHS is void ####
+ # outside def: runtime error
+ lines =<< trim END
+ vim9script
+ var Fn3a: func = (): void => {
+ }
+ var x = Fn3a()
+ END
+ v9.CheckScriptFailure(lines, 'E1031: Cannot use void value')
+
+ # inside def, compile-time error (known void return)
+ lines =<< trim END
+ vim9script
+ def Fn3b(): void
+ enddef
+ def TestFunc()
+ var x = Fn3b()
+ enddef
+ defcompile TestFunc
+ END
+ v9.CheckScriptFailure(lines, 'E1031: Cannot use void value')
+
+ # inside def, runtime error (untyped func)
+ lines =<< trim END
+ vim9script
+ def TestFunc()
+ var Fn3c: func = (): void => {
+ }
+ var x = Fn3c()
+ enddef
+ TestFunc()
+ END
+ v9.CheckScriptFailure(lines, 'E1031: Cannot use void value')
+
+ # inside def, compile-time error (func(): void)
+ lines =<< trim END
+ vim9script
+ def TestFunc()
+ var Fn3d: func(): void = () => {
+ }
+ var x = Fn3d()
+ enddef
+ defcompile TestFunc
+ END
+ v9.CheckScriptFailure(lines, 'E1031: Cannot use void value')
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 25442abc6..c9db272bb 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 */
+/**/
+ 292,
/**/
291,
/**/
diff --git a/src/vim9execute.c b/src/vim9execute.c
index f7d0cc3c3..1bc25ed98 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -1412,6 +1412,17 @@ call_bfunc(int func_idx, int argcount, ectx_T *ectx)

if (call_prepare(argcount, argvars, ectx) == FAIL)
return FAIL;
+
+ // Check for void value being passed as an argument.
+ for (idx = 0; idx < argcount; ++idx)
+ if (argvars[idx].v_type == VAR_VOID)
+ {
+ emsg(_(e_cannot_use_void_value));
+ for (idx = 0; idx < argcount; ++idx)
+ clear_tv(&argvars[idx]);
+ return FAIL;
+ }
+
ectx->ec_where.wt_func_name = internal_func_name(func_idx);

// Call the builtin function. Set "current_ectx" so that when it
@@ -4307,8 +4318,11 @@ exec_instructions(ectx_T *ectx)
case ISN_STORE:
--ectx->ec_stack.ga_len;
tv = STACK_TV_VAR(iptr->isn_arg.number);
- if (check_typval_is_value(STACK_TV_BOT(0)) == FAIL)
+ if (check_typval_is_value(STACK_TV_BOT(0)) == FAIL
+ || STACK_TV_BOT(0)->v_type == VAR_VOID)
{
+ if (STACK_TV_BOT(0)->v_type == VAR_VOID)
+ emsg(_(e_cannot_use_void_value));
clear_tv(STACK_TV_BOT(0));
goto on_error;
}
diff --git a/src/vim9expr.c b/src/vim9expr.c
index e12c87edd..0344976c9 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -2568,6 +2568,13 @@ compile_subscript(
return FAIL;
ppconst->pp_is_const = FALSE;

+ type = get_type_on_stack(cctx, 0);
+ if (type->tt_type == VAR_VOID)
+ {
+ emsg(_(e_cannot_use_void_value));
+ return FAIL;
+ }
+
// Apply the '!', '-' and '+' first:
// -1.0->func() works like (-1.0)->func()
if (compile_leader(cctx, TRUE, start_leader, end_leader) == FAIL)
Reply all
Reply to author
Forward
0 new messages