]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0292: E340 internal error when using method call on void value v9.2.0292
authorHirohito Higashi <h.east.727@gmail.com>
Sat, 4 Apr 2026 08:27:46 +0000 (08:27 +0000)
committerChristian Brabandt <cb@256bit.org>
Sat, 4 Apr 2026 08:31:21 +0000 (08:31 +0000)
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>
src/eval.c
src/testdir/test_vim9_builtin.vim
src/testdir/test_vim9_expr.vim
src/testdir/test_vim9_func.vim
src/version.c
src/vim9execute.c
src/vim9expr.c

index e8b9cc83033cf704b6aaeede6eef3215841d52b8..6f7c9960a29ea501e541cbdb5dc7834f1d793a7a 100644 (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;
index 4cc5656289f9d614cde68776b734d04c1d951c03..d8f69eff3eb342d552b823a0889c21afd30adcfc 100644 (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'])
index ab0b02a6d190712a83e783e570154edee7dc828e..bd0dca335f9a68d8af0293675435505daab1ae35 100644 (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')]
index 343c2f777ee504ac5b3e5ff2939e197f26efeefc..f4cd1edaab8d38b2b245f3cc274961b6364bb5be 100644 (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
index 25442abc6166fada5a5fbbf1944e492c572f6f87..c9db272bbbc66b99551cf1f5a67826d6c81b0146 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    292,
 /**/
     291,
 /**/
index f7d0cc3c3cd47156114ca88f68b74a1fa435d659..1bc25ed98e03f33de961472d94194570d4e5b401 100644 (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;
                }
index e12c87edd0586857baf07a5c36e97ecf186ac416..0344976c9d48d33b84550246b3833c49286a9b03 100644 (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)