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 <[email protected]>
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 <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

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)

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1w8wOO-003QRM-C1%40256bit.org.

Raspunde prin e-mail lui