From: Hirohito Higashi Date: Sat, 4 Apr 2026 08:27:46 +0000 (+0000) Subject: patch 9.2.0292: E340 internal error when using method call on void value X-Git-Tag: v9.2.0292^0 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=a9d01da6616a702ec093b0abc75e097368889524;p=thirdparty%2Fvim.git 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 Signed-off-by: Christian Brabandt --- diff --git a/src/eval.c b/src/eval.c index e8b9cc8303..6f7c9960a2 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 4cc5656289..d8f69eff3e 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 ab0b02a6d1..bd0dca335f 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 343c2f777e..f4cd1edaab 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 25442abc61..c9db272bbb 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 f7d0cc3c3c..1bc25ed98e 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 e12c87edd0..0344976c9d 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)