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.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Hirohito Higashi
2026-04-04 08:27:46 +00:00
committed by Christian Brabandt
parent cb51add7ae
commit a9d01da661
7 changed files with 174 additions and 5 deletions
+7 -1
View File
@@ -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;
+2 -2
View File
@@ -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'])
+1 -1
View File
@@ -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')]
+140
View File
@@ -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
+2
View File
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
292,
/**/
291,
/**/
+15 -1
View File
@@ -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;
}
+7
View File
@@ -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)