patch 9.1.1546: Vim9: error with has() and short circuit evaluation

Commit: 
https://github.com/vim/vim/commit/8de753148f6300aa00f0c3c5dacec3b1ca886c34
Author: Yegappan Lakshmanan <yegap...@yahoo.com>
Date:   Tue Jul 15 20:26:52 2025 +0200

    patch 9.1.1546: Vim9: error with has() and short circuit evaluation
    
    Problem:  Vim9: error with has() and short circuit evaluation
    Solution: Only eval, if ctx_skip is not SKIP_YES (Yegappan Lakshmanan).
    
    fixes: #17750
    closes: #17755
    
    Signed-off-by: Yegappan Lakshmanan <yegap...@yahoo.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/testdir/test_vim9_disassemble.vim 
b/src/testdir/test_vim9_disassemble.vim
index 4a86a9b56..04b5e1118 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -3875,4 +3875,131 @@ def Test_disassemble_assign_tuple_set_type()
   unlet g:instr
 enddef
 
+" Disassemble the code generated for a has() function call
+def Test_disassemble_has_shortcircuit()
+  # true && false condition check
+  var lines =<< trim END
+    vim9script
+    def Fn(): string
+      if has('jumplist') && has('foobar')
+        return 'present'
+      endif
+      return 'missing'
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'if has(''jumplist'') && has(''foobar'')\_s*' ..
+    'return ''present''\_s*' ..
+    'endif\_s*' ..
+    'return ''missing''\_s*' ..
+    '0 PUSHS "missing"\_s*' ..
+    '1 RETURN', g:instr)
+
+  # false && true condition check
+  lines =<< trim END
+    vim9script
+    def Fn(): string
+      if has('foobar') && has('jumplist')
+        return 'present'
+      endif
+      return 'missing'
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'if has(''foobar'') && has(''jumplist'')\_s*' ..
+    'return ''present''\_s*' ..
+    'endif\_s*' ..
+    'return ''missing''\_s*' ..
+    '0 PUSHS "missing"\_s*' ..
+    '1 RETURN', g:instr)
+
+  # false && false condition check
+  lines =<< trim END
+    vim9script
+    def Fn(): string
+      if has('foobar') && has('foobaz')
+        return 'present'
+      endif
+      return 'missing'
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'if has(''foobar'') && has(''foobaz'')\_s*' ..
+    'return ''present''\_s*' ..
+    'endif\_s*' ..
+    'return ''missing''\_s*' ..
+    '0 PUSHS "missing"\_s*' ..
+    '1 RETURN', g:instr)
+
+  # true || false condition check
+  lines =<< trim END
+    vim9script
+    def Fn(): string
+      if has('jumplist') || has('foobar')
+        return 'present'
+      endif
+      return 'missing'
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'if has(''jumplist'') || has(''foobar'')\_s*' ..
+    'return ''present''\_s*' ..
+    '0 PUSHS "present"\_s*' ..
+    '1 RETURN\_s*' ..
+    'endif\_s*' ..
+    'return ''missing''\_s*' ..
+    '2 PUSHS "missing"\_s*' ..
+    '3 RETURN', g:instr)
+
+  # false || true condition check
+  lines =<< trim END
+    vim9script
+    def Fn(): string
+      if has('foobar') || has('jumplist')
+        return 'present'
+      endif
+      return 'missing'
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'if has(''foobar'') || has(''jumplist'')\_s*' ..
+    'return ''present''\_s*' ..
+    '0 PUSHS "present"\_s*' ..
+    '1 RETURN\_s*' ..
+    'endif\_s*' ..
+    'return ''missing''\_s*' ..
+    '2 PUSHS "missing"\_s*' ..
+    '3 RETURN', g:instr)
+
+  # false || false condition check
+  lines =<< trim END
+    vim9script
+    def Fn(): string
+      if has('foobar') || has('foobaz')
+        return 'present'
+      endif
+      return 'missing'
+    enddef
+    g:instr = execute('disassemble Fn')
+  END
+  v9.CheckScriptSuccess(lines)
+  assert_match('<SNR>\d\+_Fn\_s*' ..
+    'if has(''foobar'') || has(''foobaz'')\_s*' ..
+    'return ''present''\_s*' ..
+    'endif\_s*' ..
+    'return ''missing''\_s*' ..
+    '0 PUSHS "missing"\_s*' ..
+    '1 RETURN', g:instr)
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index d4043d1db..2e70a3ba9 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -5265,6 +5265,102 @@ def Test_method_call_with_list_arg()
   v9.CheckSourceSuccess(lines)
 enddef
 
+" Test for using more than one has() check in a compound if condition.
+def Test_has_func_shortcircuit()
+  def Has_And1_Cond(): string
+    # true && false
+    if has('jumplist') && has('foobar')
+      return 'present'
+    endif
+    return 'missing'
+  enddef
+  assert_equal('missing', Has_And1_Cond())
+
+  def Has_And2_Cond(): string
+    # false && true
+    if has('foobar') && has('jumplist')
+      return 'present'
+    endif
+    return 'missing'
+  enddef
+  assert_equal('missing', Has_And2_Cond())
+
+  def Has_And3_Cond(): string
+    # false && false
+    if has('foobar') && has('foobaz')
+      return 'present'
+    endif
+    return 'missing'
+  enddef
+  assert_equal('missing', Has_And3_Cond())
+
+  def Has_Or1_Cond(): string
+    # true || false
+    if has('jumplist') || has('foobar')
+      return 'present'
+    endif
+    return 'missing'
+  enddef
+  assert_equal('present', Has_Or1_Cond())
+
+  def Has_Or2_Cond(): string
+    # false || true
+    if has('foobar') || has('jumplist')
+      return 'present'
+    endif
+    return 'missing'
+  enddef
+  assert_equal('present', Has_Or2_Cond())
+
+  def Has_Or3_Cond(): string
+    # false || false
+    if has('foobar') || has('foobaz')
+      return 'present'
+    endif
+    return 'missing'
+  enddef
+  assert_equal('missing', Has_Or3_Cond())
+enddef
+
+" Test for using more than one len() function in a compound if condition.
+def Test_len_func_shortcircuit()
+  def Len_And1_Cond(): string
+    # true && false
+    if len('xxx') == 3 && len('yyy') == 2
+      return 'match'
+    endif
+    return 'nomatch'
+  enddef
+  assert_equal('nomatch', Len_And1_Cond())
+
+  def Len_And2_Cond(): string
+    # false && true
+    if len('xxx') == 2 && len('yyy') == 3
+      return 'match'
+    endif
+    return 'nomatch'
+  enddef
+  assert_equal('nomatch', Len_And2_Cond())
+
+  def Len_Or1_Cond(): string
+    # true || false
+    if len('xxx') == 3 || len('yyy') == 2
+      return 'match'
+    endif
+    return 'nomatch'
+  enddef
+  assert_equal('match', Len_Or1_Cond())
+
+  def Len_Or2_Cond(): string
+    # false || true
+    if len('xxx') == 2 || len('yyy') == 3
+      return 'match'
+    endif
+    return 'nomatch'
+  enddef
+  assert_equal('match', Len_Or2_Cond())
+enddef
+
 " Keep this last, it messes up highlighting.
 def Test_substitute_cmd()
   new
diff --git a/src/version.c b/src/version.c
index e4084d585..16b71bb68 100644
--- a/src/version.c
+++ b/src/version.c
@@ -719,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1546,
 /**/
     1545,
 /**/
diff --git a/src/vim9expr.c b/src/vim9expr.c
index d169ed75a..33c0dafb7 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -1233,20 +1233,25 @@ compile_call(
               && ((is_has && !dynamic_feature(argvars[0].vval.v_string))
                    || !is_has))
        {
-           typval_T    *tv = &ppconst->pp_tv[ppconst->pp_used];
 
            *arg = s + 1;
-           argvars[1].v_type = VAR_UNKNOWN;
-           tv->v_type = VAR_NUMBER;
-           tv->vval.v_number = 0;
-           if (is_has)
-               f_has(argvars, tv);
-           else if (is_len)
-               f_len(argvars, tv);
-           else
-               f_exists(argvars, tv);
+
+           if (cctx->ctx_skip != SKIP_YES)
+           {
+               typval_T        *tv = &ppconst->pp_tv[ppconst->pp_used];
+
+               argvars[1].v_type = VAR_UNKNOWN;
+               tv->v_type = VAR_NUMBER;
+               tv->vval.v_number = 0;
+               if (is_has)
+                   f_has(argvars, tv);
+               else if (is_len)
+                   f_len(argvars, tv);
+               else
+                   f_exists(argvars, tv);
+               ++ppconst->pp_used;
+           }
            clear_tv(&argvars[0]);
-           ++ppconst->pp_used;
            return OK;
        }
        clear_tv(&argvars[0]);

-- 
-- 
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 vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1ubkeF-007dbI-HM%40256bit.org.

Raspunde prin e-mail lui