Patch 8.2.0719
Problem:    Vim9: more expressions can be evaluated at compile time
Solution:   Recognize has('name').
Files:      src/vim9compile.c, src/testdir/test_vim9_disassemble.vim,
            src/testdir/test_vim9_expr.vim


*** ../vim-8.2.0718/src/vim9compile.c   2020-05-08 19:10:30.782336716 +0200
--- src/vim9compile.c   2020-05-09 15:34:32.888630958 +0200
***************
*** 136,144 ****
  static char e_var_notfound[] = N_("E1001: variable not found: %s");
  static char e_syntax_at[] = N_("E1002: Syntax error at %s");
  
! static int compile_expr1(char_u **arg,  cctx_T *cctx);
! static int compile_expr2(char_u **arg,  cctx_T *cctx);
! static int compile_expr3(char_u **arg,  cctx_T *cctx);
  static void delete_def_function_contents(dfunc_T *dfunc);
  static void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
  static int check_type(type_T *expected, type_T *actual, int give_msg);
--- 136,142 ----
  static char e_var_notfound[] = N_("E1001: variable not found: %s");
  static char e_syntax_at[] = N_("E1002: Syntax error at %s");
  
! static int compile_expr0(char_u **arg,  cctx_T *cctx);
  static void delete_def_function_contents(dfunc_T *dfunc);
  static void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
  static int check_type(type_T *expected, type_T *actual, int give_msg);
***************
*** 744,767 ****
  }
  
  /*
!  * Generate an ISN_COMPARE* instruction with a boolean result.
   */
!     static int
! generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
  {
      isntype_T isntype = ISN_DROP;
-     isn_T     *isn;
-     garray_T  *stack = &cctx->ctx_type_stack;
-     vartype_T type1;
-     vartype_T type2;
- 
-     RETURN_OK_IF_SKIP(cctx);
  
-     // Get the known type of the two items on the stack.  If they are matching
-     // use a type-specific instruction. Otherwise fall back to runtime type
-     // checking.
-     type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]->tt_type;
-     type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type;
      if (type1 == VAR_UNKNOWN)
        type1 = VAR_ANY;
      if (type2 == VAR_UNKNOWN)
--- 742,755 ----
  }
  
  /*
!  * Get the instruction to use for comparing "type1" with "type2"
!  * Return ISN_DROP when failed.
   */
!     static isntype_T
! get_compare_isn(exptype_T exptype, vartype_T type1, vartype_T type2)
  {
      isntype_T isntype = ISN_DROP;
  
      if (type1 == VAR_UNKNOWN)
        type1 = VAR_ANY;
      if (type2 == VAR_UNKNOWN)
***************
*** 796,802 ****
      {
        semsg(_("E1037: Cannot use \"%s\" with %s"),
                exptype == EXPR_IS ? "is" : "isnot" , vartype_name(type1));
!       return FAIL;
      }
      if (isntype == ISN_DROP
            || ((exptype != EXPR_EQUAL && exptype != EXPR_NEQUAL
--- 784,790 ----
      {
        semsg(_("E1037: Cannot use \"%s\" with %s"),
                exptype == EXPR_IS ? "is" : "isnot" , vartype_name(type1));
!       return ISN_DROP;
      }
      if (isntype == ISN_DROP
            || ((exptype != EXPR_EQUAL && exptype != EXPR_NEQUAL
***************
*** 809,816 ****
      {
        semsg(_("E1072: Cannot compare %s with %s"),
                vartype_name(type1), vartype_name(type2));
!       return FAIL;
      }
  
      if ((isn = generate_instr(cctx, isntype)) == NULL)
        return FAIL;
--- 797,829 ----
      {
        semsg(_("E1072: Cannot compare %s with %s"),
                vartype_name(type1), vartype_name(type2));
!       return ISN_DROP;
      }
+     return isntype;
+ }
+ 
+ /*
+  * Generate an ISN_COMPARE* instruction with a boolean result.
+  */
+     static int
+ generate_COMPARE(cctx_T *cctx, exptype_T exptype, int ic)
+ {
+     isntype_T isntype;
+     isn_T     *isn;
+     garray_T  *stack = &cctx->ctx_type_stack;
+     vartype_T type1;
+     vartype_T type2;
+ 
+     RETURN_OK_IF_SKIP(cctx);
+ 
+     // Get the known type of the two items on the stack.  If they are matching
+     // use a type-specific instruction. Otherwise fall back to runtime type
+     // checking.
+     type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]->tt_type;
+     type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type;
+     isntype = get_compare_isn(exptype, type1, type2);
+     if (isntype == ISN_DROP)
+       return FAIL;
  
      if ((isn = generate_instr(cctx, isntype)) == NULL)
        return FAIL;
***************
*** 2340,2345 ****
--- 2353,2443 ----
      return OK;
  }
  
+ // Structure passed between the compile_expr* functions to keep track of
+ // constants that have been parsed but for which no code was produced yet.  If
+ // possible expressions on these constants are applied at compile time.  If
+ // that is not possible, the code to push the constants needs to be generated
+ // before other instructions.
+ typedef struct {
+     typval_T  pp_tv[10];      // stack of ppconst constants
+     int               pp_used;        // active entries in pp_tv[]
+ } ppconst_T;
+ 
+ /*
+  * Generate a PUSH instruction for "tv".
+  * "tv" will be consumed or cleared.
+  * Nothing happens if "tv" is NULL or of type VAR_UNKNOWN;
+  */
+     static int
+ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
+ {
+     if (tv != NULL)
+     {
+       switch (tv->v_type)
+       {
+           case VAR_UNKNOWN:
+               break;
+           case VAR_BOOL:
+               generate_PUSHBOOL(cctx, tv->vval.v_number);
+               break;
+           case VAR_SPECIAL:
+               generate_PUSHSPEC(cctx, tv->vval.v_number);
+               break;
+           case VAR_NUMBER:
+               generate_PUSHNR(cctx, tv->vval.v_number);
+               break;
+ #ifdef FEAT_FLOAT
+           case VAR_FLOAT:
+               generate_PUSHF(cctx, tv->vval.v_float);
+               break;
+ #endif
+           case VAR_BLOB:
+               generate_PUSHBLOB(cctx, tv->vval.v_blob);
+               tv->vval.v_blob = NULL;
+               break;
+           case VAR_STRING:
+               generate_PUSHS(cctx, tv->vval.v_string);
+               tv->vval.v_string = NULL;
+               break;
+           default:
+               iemsg("constant type not supported");
+               clear_tv(tv);
+               return FAIL;
+       }
+       tv->v_type = VAR_UNKNOWN;
+     }
+     return OK;
+ }
+ 
+ /*
+  * Generate code for any ppconst entries.
+  */
+     static int
+ generate_ppconst(cctx_T *cctx, ppconst_T *ppconst)
+ {
+     int           i;
+     int           ret = OK;
+ 
+     for (i = 0; i < ppconst->pp_used; ++i)
+       if (generate_tv_PUSH(cctx, &ppconst->pp_tv[i]) == FAIL)
+           ret = FAIL;
+     ppconst->pp_used = 0;
+     return ret;
+ }
+ 
+ /*
+  * Clear ppconst constants.  Used when failing.
+  */
+     static void
+ clear_ppconst(ppconst_T *ppconst)
+ {
+     int           i;
+ 
+     for (i = 0; i < ppconst->pp_used; ++i)
+       clear_tv(&ppconst->pp_tv[i]);
+     ppconst->pp_used = 0;
+ }
+ 
  /*
   * Generate an instruction to load script-local variable "name", without the
   * leading "s:".
***************
*** 2526,2550 ****
            }
            else
            {
!               if ((len == 4 && STRNCMP("true", *arg, 4) == 0)
!                       || (len == 5 && STRNCMP("false", *arg, 5) == 0))
!                   res = generate_PUSHBOOL(cctx, **arg == 't'
!                                                    ? VVAL_TRUE : VVAL_FALSE);
!               else
!               {
!                   // "var" can be script-local even without using "s:" if it
!                   // already exists.
!                   if (SCRIPT_ITEM(current_sctx.sc_sid)->sn_version
!                                                       == SCRIPT_VERSION_VIM9
!                               || lookup_script(*arg, len) == OK)
!                      res = compile_load_scriptvar(cctx, name, *arg, &end,
!                                                                       FALSE);
! 
!                   // When the name starts with an uppercase letter or "x:" it
!                   // can be a user defined function.
!                   if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':'))
!                       res = generate_funcref(cctx, name);
!               }
            }
        }
        if (gen_load)
--- 2624,2641 ----
            }
            else
            {
!               // "var" can be script-local even without using "s:" if it
!               // already exists.
!               if (SCRIPT_ITEM(current_sctx.sc_sid)->sn_version
!                                                   == SCRIPT_VERSION_VIM9
!                           || lookup_script(*arg, len) == OK)
!                  res = compile_load_scriptvar(cctx, name, *arg, &end,
!                                                                   FALSE);
! 
!               // When the name starts with an uppercase letter or "x:" it
!               // can be a user defined function.
!               if (res == FAIL && (ASCII_ISUPPER(*name) || name[1] == ':'))
!                   res = generate_funcref(cctx, name);
            }
        }
        if (gen_load)
***************
*** 2588,2594 ****
            return OK;
        }
  
!       if (compile_expr1(&p, cctx) == FAIL)
            return FAIL;
        ++*argcount;
  
--- 2679,2685 ----
            return OK;
        }
  
!       if (compile_expr0(&p, cctx) == FAIL)
            return FAIL;
        ++*argcount;
  
***************
*** 2621,2627 ****
   *    BCALL / DCALL / UCALL
   */
      static int
! compile_call(char_u **arg, size_t varlen, cctx_T *cctx, int argcount_init)
  {
      char_u    *name = *arg;
      char_u    *p;
--- 2712,2723 ----
   *    BCALL / DCALL / UCALL
   */
      static int
! compile_call(
!       char_u      **arg,
!       size_t      varlen,
!       cctx_T      *cctx,
!       ppconst_T   *ppconst,
!       int         argcount_init)
  {
      char_u    *name = *arg;
      char_u    *p;
***************
*** 2633,2638 ****
--- 2729,2764 ----
      ufunc_T   *ufunc;
      int               res = FAIL;
  
+     // we can evaluate "has('name')" at compile time
+     if (varlen == 3 && STRNCMP(*arg, "has", 3) == 0)
+     {
+       char_u      *s = skipwhite(*arg + varlen + 1);
+       typval_T    argvars[2];
+ 
+       argvars[0].v_type = VAR_UNKNOWN;
+       if (*s == '"')
+           (void)get_string_tv(&s, &argvars[0], TRUE);
+       else if (*s == '\'')
+           (void)get_lit_string_tv(&s, &argvars[0], TRUE);
+       s = skipwhite(s);
+       if (*s == ')' && argvars[0].v_type == VAR_STRING)
+       {
+           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;
+           f_has(argvars, tv);
+           clear_tv(&argvars[0]);
+           ++ppconst->pp_used;
+           return OK;
+       }
+     }
+ 
+     if (generate_ppconst(cctx, ppconst) == FAIL)
+       return FAIL;
+ 
      if (varlen >= sizeof(namebuf))
      {
        semsg(_("E1011: name too long: %s"), name);
***************
*** 2791,2797 ****
                p += STRLEN(p);
            break;
        }
!       if (compile_expr1(&p, cctx) == FAIL)
            break;
        ++count;
        if (*p == ',')
--- 2917,2923 ----
                p += STRLEN(p);
            break;
        }
!       if (compile_expr0(&p, cctx) == FAIL)
            break;
        ++count;
        if (*p == ',')
***************
*** 2929,2935 ****
        {
            isn_T               *isn;
  
!           if (compile_expr1(arg, cctx) == FAIL)
                return FAIL;
            // TODO: check type is string
            isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
--- 3055,3061 ----
        {
            isn_T               *isn;
  
!           if (compile_expr0(arg, cctx) == FAIL)
                return FAIL;
            // TODO: check type is string
            isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
***************
*** 2974,2980 ****
            *arg = skipwhite(*arg);
        }
  
!       if (compile_expr1(arg, cctx) == FAIL)
            return FAIL;
        ++count;
  
--- 3100,3106 ----
            *arg = skipwhite(*arg);
        }
  
!       if (compile_expr0(arg, cctx) == FAIL)
            return FAIL;
        ++count;
  
***************
*** 3626,3715 ****
      return OK;
  }
  
! // Structure passed between the compile_expr* functions to keep track of
! // constants that have been parsed but for which no code was produced yet.  If
! // possible expressions on these constants are applied at compile time.  If
! // that is not possible, the code to push the constants needs to be generated
! // before other instructions.
! typedef struct {
!     typval_T  pp_tv[10];      // stack of ppconst constants
!     int               pp_used;        // active entries in pp_tv[]
! } ppconst_T;
! 
! /*
!  * Generate a PUSH instruction for "tv".
!  * "tv" will be consumed or cleared.
!  * Nothing happens if "tv" is NULL or of type VAR_UNKNOWN;
!  */
!     static int
! generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
! {
!     if (tv != NULL)
!     {
!       switch (tv->v_type)
!       {
!           case VAR_UNKNOWN:
!               break;
!           case VAR_BOOL:
!               generate_PUSHBOOL(cctx, tv->vval.v_number);
!               break;
!           case VAR_SPECIAL:
!               generate_PUSHSPEC(cctx, tv->vval.v_number);
!               break;
!           case VAR_NUMBER:
!               generate_PUSHNR(cctx, tv->vval.v_number);
!               break;
! #ifdef FEAT_FLOAT
!           case VAR_FLOAT:
!               generate_PUSHF(cctx, tv->vval.v_float);
!               break;
! #endif
!           case VAR_BLOB:
!               generate_PUSHBLOB(cctx, tv->vval.v_blob);
!               tv->vval.v_blob = NULL;
!               break;
!           case VAR_STRING:
!               generate_PUSHS(cctx, tv->vval.v_string);
!               tv->vval.v_string = NULL;
!               break;
!           default:
!               iemsg("constant type not supported");
!               clear_tv(tv);
!               return FAIL;
!       }
!       tv->v_type = VAR_UNKNOWN;
!     }
!     return OK;
! }
! 
! /*
!  * Generate code for any ppconst entries.
!  */
!     static int
! generate_ppconst(cctx_T *cctx, ppconst_T *ppconst)
! {
!     int           i;
!     int           ret = OK;
! 
!     for (i = 0; i < ppconst->pp_used; ++i)
!       if (generate_tv_PUSH(cctx, &ppconst->pp_tv[i]) == FAIL)
!           ret = FAIL;
!     ppconst->pp_used = 0;
!     return ret;
! }
! 
! /*
!  * Clear ppconst constants.  Used when failing.
!  */
!     static void
! clear_ppconst(ppconst_T *ppconst)
! {
!     int           i;
! 
!     for (i = 0; i < ppconst->pp_used; ++i)
!       clear_tv(&ppconst->pp_tv[i]);
!     ppconst->pp_used = 0;
! }
  
  /*
   * Compile code to apply '-', '+' and '!'.
--- 3752,3758 ----
      return OK;
  }
  
! static int compile_expr3(char_u **arg,  cctx_T *cctx, ppconst_T *ppconst);
  
  /*
   * Compile code to apply '-', '+' and '!'.
***************
*** 3825,3831 ****
                    return FAIL;
                }
                // TODO: base value may not be the first argument
!               if (compile_call(arg, p - *arg, cctx, 1) == FAIL)
                    return FAIL;
            }
        }
--- 3868,3874 ----
                    return FAIL;
                }
                // TODO: base value may not be the first argument
!               if (compile_call(arg, p - *arg, cctx, ppconst, 1) == FAIL)
                    return FAIL;
            }
        }
***************
*** 3841,3847 ****
            // TODO: more arguments
            // TODO: dict member  dict['name']
            *arg = skipwhite(*arg + 1);
!           if (compile_expr1(arg, cctx) == FAIL)
                return FAIL;
  
            if (**arg != ']')
--- 3884,3890 ----
            // TODO: more arguments
            // TODO: dict member  dict['name']
            *arg = skipwhite(*arg + 1);
!           if (compile_expr0(arg, cctx) == FAIL)
                return FAIL;
  
            if (**arg != ']')
***************
*** 3991,3996 ****
--- 4034,4067 ----
                    break;
  
        /*
+        * "true" constant
+        */
+       case 't':   if (STRNCMP(*arg, "true", 4) == 0
+                                                  && !eval_isnamec((*arg)[4]))
+                   {
+                       *arg += 4;
+                       rettv->v_type = VAR_BOOL;
+                       rettv->vval.v_number = VVAL_TRUE;
+                   }
+                   else
+                       ret = NOTDONE;
+                   break;
+ 
+       /*
+        * "false" constant
+        */
+       case 'f':   if (STRNCMP(*arg, "false", 5) == 0
+                                                  && !eval_isnamec((*arg)[5]))
+                   {
+                       *arg += 5;
+                       rettv->v_type = VAR_BOOL;
+                       rettv->vval.v_number = VVAL_FALSE;
+                   }
+                   else
+                       ret = NOTDONE;
+                   break;
+ 
+       /*
         * List: [expr, expr]
         */
        case '[':   ret = compile_list(arg, cctx);
***************
*** 4047,4053 ****
         * nested expression: (expression).
         */
        case '(':   *arg = skipwhite(*arg + 1);
!                   ret = compile_expr1(arg, cctx);     // recursive!
                    *arg = skipwhite(*arg);
                    if (**arg == ')')
                        ++*arg;
--- 4118,4124 ----
         * nested expression: (expression).
         */
        case '(':   *arg = skipwhite(*arg + 1);
!                   ret = compile_expr0(arg, cctx);     // recursive!
                    *arg = skipwhite(*arg);
                    if (**arg == ')')
                        ++*arg;
***************
*** 4074,4091 ****
        }
        start_leader = end_leader;   // don't apply again below
  
!       // A constant expression can possibly be handled compile time, return
!       // the value instead of generating code.
!       ++ppconst->pp_used;
      }
      else if (ret == NOTDONE)
      {
        char_u      *p;
        int         r;
  
-       if (generate_ppconst(cctx, ppconst) == FAIL)
-           return FAIL;
- 
        if (!eval_isnamec1(**arg))
        {
            semsg(_("E1015: Name expected: %s"), *arg);
--- 4145,4162 ----
        }
        start_leader = end_leader;   // don't apply again below
  
!       if (cctx->ctx_skip == TRUE)
!           clear_tv(rettv);
!       else
!           // A constant expression can possibly be handled compile time,
!           // return the value instead of generating code.
!           ++ppconst->pp_used;
      }
      else if (ret == NOTDONE)
      {
        char_u      *p;
        int         r;
  
        if (!eval_isnamec1(**arg))
        {
            semsg(_("E1015: Name expected: %s"), *arg);
***************
*** 4095,4103 ****
        // "name" or "name()"
        p = to_name_end(*arg, TRUE);
        if (*p == '(')
!           r = compile_call(arg, p - *arg, cctx, 0);
        else
            r = compile_load(arg, p, cctx, TRUE);
        if (r == FAIL)
            return FAIL;
      }
--- 4166,4180 ----
        // "name" or "name()"
        p = to_name_end(*arg, TRUE);
        if (*p == '(')
!       {
!           r = compile_call(arg, p - *arg, cctx, ppconst, 0);
!       }
        else
+       {
+           if (generate_ppconst(cctx, ppconst) == FAIL)
+               return FAIL;
            r = compile_load(arg, p, cctx, TRUE);
+       }
        if (r == FAIL)
            return FAIL;
      }
***************
*** 4105,4112 ****
      // Handle following "[]", ".member", etc.
      // Then deal with prefixed '-', '+' and '!', if not done already.
      if (compile_subscript(arg, cctx, &start_leader, end_leader,
!                                                            ppconst) == FAIL
!           || compile_leader(cctx, start_leader, end_leader) == FAIL)
        return FAIL;
      return OK;
  }
--- 4182,4198 ----
      // Handle following "[]", ".member", etc.
      // Then deal with prefixed '-', '+' and '!', if not done already.
      if (compile_subscript(arg, cctx, &start_leader, end_leader,
!                                                            ppconst) == FAIL)
!       return FAIL;
!     if (ppconst->pp_used > 0)
!     {
!       // apply the '!', '-' and '+' before the constant
!       rettv = &ppconst->pp_tv[ppconst->pp_used - 1];
!       if (apply_leader(rettv, start_leader, end_leader) == FAIL)
!           return FAIL;
!       return OK;
!     }
!     if (compile_leader(cctx, start_leader, end_leader) == FAIL)
        return FAIL;
      return OK;
  }
***************
*** 4117,4126 ****
   *    %       number modulo
   */
      static int
! compile_expr6(
!       char_u      **arg,
!       cctx_T      *cctx,
!       ppconst_T   *ppconst)
  {
      char_u    *op;
      int               ppconst_used = ppconst->pp_used;
--- 4203,4209 ----
   *    %       number modulo
   */
      static int
! compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
  {
      char_u    *op;
      int               ppconst_used = ppconst->pp_used;
***************
*** 4191,4211 ****
   *      ..    string concatenation
   */
      static int
! compile_expr5(char_u **arg, cctx_T *cctx)
  {
      char_u    *op;
      int               oplen;
!     ppconst_T ppconst;
!     int               ppconst_used = 0;
! 
!     CLEAR_FIELD(ppconst);
  
      // get the first variable
!     if (compile_expr6(arg, cctx, &ppconst) == FAIL)
!     {
!       clear_ppconst(&ppconst);
        return FAIL;
-     }
  
      /*
       * Repeat computing, until no "+", "-" or ".." is following.
--- 4274,4288 ----
   *      ..    string concatenation
   */
      static int
! compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
  {
      char_u    *op;
      int               oplen;
!     int               ppconst_used = ppconst->pp_used;
  
      // get the first variable
!     if (compile_expr6(arg, cctx, ppconst) == FAIL)
        return FAIL;
  
      /*
       * Repeat computing, until no "+", "-" or ".." is following.
***************
*** 4221,4227 ****
        {
            char_u buf[3];
  
-           clear_ppconst(&ppconst);
            vim_strncpy(buf, op, oplen);
            semsg(_(e_white_both), buf);
            return FAIL;
--- 4298,4303 ----
***************
*** 4229,4255 ****
  
        *arg = skipwhite(op + oplen);
        if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
-       {
-           clear_ppconst(&ppconst);
            return FAIL;
-       }
  
        // get the second expression
!       if (compile_expr6(arg, cctx, &ppconst) == FAIL)
!       {
!           clear_ppconst(&ppconst);
            return FAIL;
-       }
  
!       if (ppconst.pp_used == ppconst_used + 2
                && (*op == '.'
!                   ? (ppconst.pp_tv[ppconst_used].v_type == VAR_STRING
!                   && ppconst.pp_tv[ppconst_used + 1].v_type == VAR_STRING)
!                   : (ppconst.pp_tv[ppconst_used].v_type == VAR_NUMBER
!                   && ppconst.pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)))
        {
!           typval_T *tv1 = &ppconst.pp_tv[ppconst_used];
!           typval_T *tv2 = &ppconst.pp_tv[ppconst_used + 1];
  
            // concat/subtract/add constant numbers
            if (*op == '+')
--- 4305,4325 ----
  
        *arg = skipwhite(op + oplen);
        if (may_get_next_line(op + oplen, arg, cctx) == FAIL)
            return FAIL;
  
        // get the second expression
!       if (compile_expr6(arg, cctx, ppconst) == FAIL)
            return FAIL;
  
!       if (ppconst->pp_used == ppconst_used + 2
                && (*op == '.'
!                   ? (ppconst->pp_tv[ppconst_used].v_type == VAR_STRING
!                   && ppconst->pp_tv[ppconst_used + 1].v_type == VAR_STRING)
!                   : (ppconst->pp_tv[ppconst_used].v_type == VAR_NUMBER
!                   && ppconst->pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)))
        {
!           typval_T *tv1 = &ppconst->pp_tv[ppconst_used];
!           typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1];
  
            // concat/subtract/add constant numbers
            if (*op == '+')
***************
*** 4266,4272 ****
                tv1->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
                if (tv1->vval.v_string == NULL)
                {
!                   clear_ppconst(&ppconst);
                    return FAIL;
                }
                mch_memmove(tv1->vval.v_string, s1, len1);
--- 4336,4342 ----
                tv1->vval.v_string = alloc((int)(len1 + STRLEN(s2) + 1));
                if (tv1->vval.v_string == NULL)
                {
!                   clear_ppconst(ppconst);
                    return FAIL;
                }
                mch_memmove(tv1->vval.v_string, s1, len1);
***************
*** 4274,4292 ****
                vim_free(s1);
                vim_free(s2);
            }
!           --ppconst.pp_used;
        }
        else
        {
!           generate_ppconst(cctx, &ppconst);
            if (*op == '.')
            {
                if (may_generate_2STRING(-2, cctx) == FAIL
                        || may_generate_2STRING(-1, cctx) == FAIL)
-               {
-                   clear_ppconst(&ppconst);
                    return FAIL;
-               }
                generate_instr_drop(cctx, ISN_CONCAT, 1);
            }
            else
--- 4344,4359 ----
                vim_free(s1);
                vim_free(s2);
            }
!           --ppconst->pp_used;
        }
        else
        {
!           generate_ppconst(cctx, ppconst);
            if (*op == '.')
            {
                if (may_generate_2STRING(-2, cctx) == FAIL
                        || may_generate_2STRING(-1, cctx) == FAIL)
                    return FAIL;
                generate_instr_drop(cctx, ISN_CONCAT, 1);
            }
            else
***************
*** 4294,4302 ****
        }
      }
  
-     // TODO: move to caller
-     generate_ppconst(cctx, &ppconst);
- 
      return OK;
  }
  
--- 4361,4366 ----
***************
*** 4318,4332 ****
   *    COMPARE                 one of the compare instructions
   */
      static int
! compile_expr4(char_u **arg, cctx_T *cctx)
  {
      exptype_T type = EXPR_UNKNOWN;
      char_u    *p;
      int               len = 2;
      int               type_is = FALSE;
  
      // get the first variable
!     if (compile_expr5(arg, cctx) == FAIL)
        return FAIL;
  
      p = skipwhite(*arg);
--- 4382,4397 ----
   *    COMPARE                 one of the compare instructions
   */
      static int
! compile_expr4(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
  {
      exptype_T type = EXPR_UNKNOWN;
      char_u    *p;
      int               len = 2;
      int               type_is = FALSE;
+     int               ppconst_used = ppconst->pp_used;
  
      // get the first variable
!     if (compile_expr5(arg, cctx, ppconst) == FAIL)
        return FAIL;
  
      p = skipwhite(*arg);
***************
*** 4369,4378 ****
        if (may_get_next_line(p + len, arg, cctx) == FAIL)
            return FAIL;
  
!       if (compile_expr5(arg, cctx) == FAIL)
            return FAIL;
  
!       generate_COMPARE(cctx, type, ic);
      }
  
      return OK;
--- 4434,4467 ----
        if (may_get_next_line(p + len, arg, cctx) == FAIL)
            return FAIL;
  
!       if (compile_expr5(arg, cctx, ppconst) == FAIL)
            return FAIL;
  
!       if (ppconst->pp_used == ppconst_used + 2)
!       {
!           typval_T *  tv1 = &ppconst->pp_tv[ppconst->pp_used - 2];
!           typval_T    *tv2 = &ppconst->pp_tv[ppconst->pp_used - 1];
!           int         ret;
! 
!           // Both sides are a constant, compute the result now.
!           // First check for a valid combination of types, this is more
!           // strict than typval_compare().
!           if (get_compare_isn(type, tv1->v_type, tv2->v_type) == ISN_DROP)
!               ret = FAIL;
!           else
!           {
!               ret = typval_compare(tv1, tv2, type, ic);
!               tv1->v_type = VAR_BOOL;
!               tv1->vval.v_number = tv1->vval.v_number
!                                                     ? VVAL_TRUE : VVAL_FALSE;
!               clear_tv(tv2);
!               --ppconst->pp_used;
!           }
!           return ret;
!       }
! 
!       generate_ppconst(cctx, ppconst);
!       return generate_COMPARE(cctx, type, ic);
      }
  
      return OK;
***************
*** 4382,4388 ****
   * Compile || or &&.
   */
      static int
! compile_and_or(char_u **arg, cctx_T *cctx, char *op)
  {
      char_u    *p = skipwhite(*arg);
      int               opchar = *op;
--- 4471,4482 ----
   * Compile || or &&.
   */
      static int
! compile_and_or(
!       char_u **arg,
!       cctx_T  *cctx,
!       char    *op,
!       ppconst_T *ppconst,
!       int     ppconst_used UNUSED)
  {
      char_u    *p = skipwhite(*arg);
      int               opchar = *op;
***************
*** 4404,4409 ****
--- 4498,4506 ----
                return FAIL;
            }
  
+           // TODO: use ppconst if the value is a constant
+           generate_ppconst(cctx, ppconst);
+ 
            if (ga_grow(&end_ga, 1) == FAIL)
            {
                ga_clear(&end_ga);
***************
*** 4419,4432 ****
            if (may_get_next_line(p + 2, arg, cctx) == FAIL)
                return FAIL;
  
!           if ((opchar == '|' ? compile_expr3(arg, cctx)
!                                          : compile_expr4(arg, cctx)) == FAIL)
            {
                ga_clear(&end_ga);
                return FAIL;
            }
            p = skipwhite(*arg);
        }
  
        // Fill in the end label in all jumps.
        while (end_ga.ga_len > 0)
--- 4516,4530 ----
            if (may_get_next_line(p + 2, arg, cctx) == FAIL)
                return FAIL;
  
!           if ((opchar == '|' ? compile_expr3(arg, cctx, ppconst)
!                                 : compile_expr4(arg, cctx, ppconst)) == FAIL)
            {
                ga_clear(&end_ga);
                return FAIL;
            }
            p = skipwhite(*arg);
        }
+       generate_ppconst(cctx, ppconst);
  
        // Fill in the end label in all jumps.
        while (end_ga.ga_len > 0)
***************
*** 4456,4469 ****
   * end:
   */
      static int
! compile_expr3(char_u **arg, cctx_T *cctx)
  {
      // get the first variable
!     if (compile_expr4(arg, cctx) == FAIL)
        return FAIL;
  
      // || and && work almost the same
!     return compile_and_or(arg, cctx, "&&");
  }
  
  /*
--- 4554,4569 ----
   * end:
   */
      static int
! compile_expr3(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
  {
+     int               ppconst_used = ppconst->pp_used;
+ 
      // get the first variable
!     if (compile_expr4(arg, cctx, ppconst) == FAIL)
        return FAIL;
  
      // || and && work almost the same
!     return compile_and_or(arg, cctx, "&&", ppconst, ppconst_used);
  }
  
  /*
***************
*** 4478,4491 ****
   * end:
   */
      static int
! compile_expr2(char_u **arg, cctx_T *cctx)
  {
      // eval the first expression
!     if (compile_expr3(arg, cctx) == FAIL)
        return FAIL;
  
      // || and && work almost the same
!     return compile_and_or(arg, cctx, "||");
  }
  
  /*
--- 4578,4593 ----
   * end:
   */
      static int
! compile_expr2(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
  {
+     int               ppconst_used = ppconst->pp_used;
+ 
      // eval the first expression
!     if (compile_expr3(arg, cctx, ppconst) == FAIL)
        return FAIL;
  
      // || and && work almost the same
!     return compile_and_or(arg, cctx, "||", ppconst, ppconst_used);
  }
  
  /*
***************
*** 4500,4511 ****
   * end:
   */
      static int
! compile_expr1(char_u **arg,  cctx_T *cctx)
  {
      char_u    *p;
  
      // Evaluate the first expression.
!     if (compile_expr2(arg, cctx) == FAIL)
        return FAIL;
  
      p = skipwhite(*arg);
--- 4602,4614 ----
   * end:
   */
      static int
! compile_expr1(char_u **arg,  cctx_T *cctx, ppconst_T *ppconst)
  {
      char_u    *p;
+     int               ppconst_used = ppconst->pp_used;
  
      // Evaluate the first expression.
!     if (compile_expr2(arg, cctx, ppconst) == FAIL)
        return FAIL;
  
      p = skipwhite(*arg);
***************
*** 4518,4523 ****
--- 4621,4629 ----
        isn_T           *isn;
        type_T          *type1;
        type_T          *type2;
+       int             has_const_expr = FALSE;
+       int             const_value = FALSE;
+       int             save_skip = cctx->ctx_skip;
  
        if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1]))
        {
***************
*** 4525,4550 ****
            return FAIL;
        }
  
!       generate_JUMP(cctx, JUMP_IF_FALSE, 0);
  
        // evaluate the second expression; any type is accepted
        *arg = skipwhite(p + 1);
        if (may_get_next_line(p + 1, arg, cctx) == FAIL)
            return FAIL;
! 
!       if (compile_expr1(arg, cctx) == FAIL)
            return FAIL;
  
!       // remember the type and drop it
!       --stack->ga_len;
!       type1 = ((type_T **)stack->ga_data)[stack->ga_len];
  
!       end_idx = instr->ga_len;
!       generate_JUMP(cctx, JUMP_ALWAYS, 0);
  
!       // jump here from JUMP_IF_FALSE
!       isn = ((isn_T *)instr->ga_data) + alt_idx;
!       isn->isn_arg.jump.jump_where = instr->ga_len;
  
        // Check for the ":".
        p = skipwhite(*arg);
--- 4631,4674 ----
            return FAIL;
        }
  
!       if (ppconst->pp_used == ppconst_used + 1)
!       {
!           // the condition is a constant, we know whether the ? or the :
!           // expression is to be evaluated.
!           has_const_expr = TRUE;
!           const_value = tv2bool(&ppconst->pp_tv[ppconst_used]);
!           clear_tv(&ppconst->pp_tv[ppconst_used]);
!           --ppconst->pp_used;
!           cctx->ctx_skip = save_skip == TRUE || !const_value;
!       }
!       else
!       {
!           generate_ppconst(cctx, ppconst);
!           generate_JUMP(cctx, JUMP_IF_FALSE, 0);
!       }
  
        // evaluate the second expression; any type is accepted
        *arg = skipwhite(p + 1);
        if (may_get_next_line(p + 1, arg, cctx) == FAIL)
            return FAIL;
!       if (compile_expr1(arg, cctx, ppconst) == FAIL)
            return FAIL;
  
!       if (!has_const_expr)
!       {
!           generate_ppconst(cctx, ppconst);
  
!           // remember the type and drop it
!           --stack->ga_len;
!           type1 = ((type_T **)stack->ga_data)[stack->ga_len];
  
!           end_idx = instr->ga_len;
!           generate_JUMP(cctx, JUMP_ALWAYS, 0);
! 
!           // jump here from JUMP_IF_FALSE
!           isn = ((isn_T *)instr->ga_data) + alt_idx;
!           isn->isn_arg.jump.jump_where = instr->ga_len;
!       }
  
        // Check for the ":".
        p = skipwhite(*arg);
***************
*** 4560,4584 ****
        }
  
        // evaluate the third expression
        *arg = skipwhite(p + 1);
        if (may_get_next_line(p + 1, arg, cctx) == FAIL)
            return FAIL;
! 
!       if (compile_expr1(arg, cctx) == FAIL)
            return FAIL;
  
!       // If the types differ, the result has a more generic type.
!       type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
!       common_type(type1, type2, &type2, cctx->ctx_type_list);
  
!       // jump here from JUMP_ALWAYS
!       isn = ((isn_T *)instr->ga_data) + end_idx;
!       isn->isn_arg.jump.jump_where = instr->ga_len;
      }
      return OK;
  }
  
  /*
   * compile "return [expr]"
   */
      static char_u *
--- 4684,4735 ----
        }
  
        // evaluate the third expression
+       if (has_const_expr)
+           cctx->ctx_skip = save_skip == TRUE || const_value;
        *arg = skipwhite(p + 1);
        if (may_get_next_line(p + 1, arg, cctx) == FAIL)
            return FAIL;
!       if (compile_expr1(arg, cctx, ppconst) == FAIL)
            return FAIL;
  
!       if (!has_const_expr)
!       {
!           generate_ppconst(cctx, ppconst);
! 
!           // If the types differ, the result has a more generic type.
!           type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
!           common_type(type1, type2, &type2, cctx->ctx_type_list);
  
!           // jump here from JUMP_ALWAYS
!           isn = ((isn_T *)instr->ga_data) + end_idx;
!           isn->isn_arg.jump.jump_where = instr->ga_len;
!       }
! 
!       cctx->ctx_skip = save_skip;
      }
      return OK;
  }
  
  /*
+  * Toplevel expression.
+  */
+     static int
+ compile_expr0(char_u **arg,  cctx_T *cctx)
+ {
+     ppconst_T ppconst;
+ 
+     CLEAR_FIELD(ppconst);
+     if (compile_expr1(arg, cctx, &ppconst) == FAIL)
+     {
+       clear_ppconst(&ppconst);
+       return FAIL;
+     }
+     if (generate_ppconst(cctx, &ppconst) == FAIL)
+       return FAIL;
+     return OK;
+ }
+ 
+ /*
   * compile "return [expr]"
   */
      static char_u *
***************
*** 4591,4597 ****
      if (*p != NUL && *p != '|' && *p != '\n')
      {
        // compile return argument into instructions
!       if (compile_expr1(&p, cctx) == FAIL)
            return NULL;
  
        stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
--- 4742,4748 ----
      if (*p != NUL && *p != '|' && *p != '\n')
      {
        // compile return argument into instructions
!       if (compile_expr0(&p, cctx) == FAIL)
            return NULL;
  
        stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
***************
*** 5075,5081 ****
            --cctx->ctx_locals.ga_len;
        instr_count = instr->ga_len;
        p = skipwhite(p + oplen);
!       r = compile_expr1(&p, cctx);
        if (new_local)
            ++cctx->ctx_locals.ga_len;
        if (r == FAIL)
--- 5226,5232 ----
            --cctx->ctx_locals.ga_len;
        instr_count = instr->ga_len;
        p = skipwhite(p + oplen);
!       r = compile_expr0(&p, cctx);
        if (new_local)
            ++cctx->ctx_locals.ga_len;
        if (r == FAIL)
***************
*** 5526,5545 ****
  {
      char_u    *p = arg;
      garray_T  *instr = &cctx->ctx_instr;
      scope_T   *scope;
!     typval_T  tv;
  
!     // compile "expr"; if we know it evaluates to FALSE skip the block
!     tv.v_type = VAR_UNKNOWN;
!     if (evaluate_const_expr1(&p, cctx, &tv) == OK)
!       cctx->ctx_skip = tv2bool(&tv) ? FALSE : TRUE;
      else
-       cctx->ctx_skip = MAYBE;
-     clear_tv(&tv);
-     if (cctx->ctx_skip == MAYBE)
      {
!       p = arg;
!       if (compile_expr1(&p, cctx) == FAIL)
            return NULL;
      }
  
--- 5677,5704 ----
  {
      char_u    *p = arg;
      garray_T  *instr = &cctx->ctx_instr;
+     int               instr_count = instr->ga_len;
      scope_T   *scope;
!     ppconst_T ppconst;
  
!     CLEAR_FIELD(ppconst);
!     if (compile_expr1(&p, cctx, &ppconst) == FAIL)
!     {
!       clear_ppconst(&ppconst);
!       return NULL;
!     }
!     if (instr->ga_len == instr_count && ppconst.pp_used == 1)
!     {
!       // The expression results in a constant.
!       // TODO: how about nesting?
!       cctx->ctx_skip = tv2bool(&ppconst.pp_tv[0]) ? FALSE : TRUE;
!       clear_ppconst(&ppconst);
!     }
      else
      {
!       // Not a constant, generate instructions for the expression.
!       cctx->ctx_skip = MAYBE;
!       if (generate_ppconst(cctx, &ppconst) == FAIL)
            return NULL;
      }
  
***************
*** 5595,5601 ****
      if (cctx->ctx_skip == MAYBE)
      {
        p = arg;
!       if (compile_expr1(&p, cctx) == FAIL)
            return NULL;
  
        // "where" is set when ":elseif", "else" or ":endif" is found
--- 5754,5760 ----
      if (cctx->ctx_skip == MAYBE)
      {
        p = arg;
!       if (compile_expr0(&p, cctx) == FAIL)
            return NULL;
  
        // "where" is set when ":elseif", "else" or ":endif" is found
***************
*** 5754,5760 ****
  
      // compile "expr", it remains on the stack until "endfor"
      arg = p;
!     if (compile_expr1(&arg, cctx) == FAIL)
      {
        drop_scope(cctx);
        return NULL;
--- 5913,5919 ----
  
      // compile "expr", it remains on the stack until "endfor"
      arg = p;
!     if (compile_expr0(&arg, cctx) == FAIL)
      {
        drop_scope(cctx);
        return NULL;
***************
*** 5844,5850 ****
      scope->se_u.se_while.ws_top_label = instr->ga_len;
  
      // compile "expr"
!     if (compile_expr1(&p, cctx) == FAIL)
        return NULL;
  
      // "while_end" is set when ":endwhile" is found
--- 6003,6009 ----
      scope->se_u.se_while.ws_top_label = instr->ga_len;
  
      // compile "expr"
!     if (compile_expr0(&p, cctx) == FAIL)
        return NULL;
  
      // "while_end" is set when ":endwhile" is found
***************
*** 6219,6225 ****
  {
      char_u *p = skipwhite(arg);
  
!     if (compile_expr1(&p, cctx) == FAIL)
        return NULL;
      if (may_generate_2STRING(-1, cctx) == FAIL)
        return NULL;
--- 6378,6384 ----
  {
      char_u *p = skipwhite(arg);
  
!     if (compile_expr0(&p, cctx) == FAIL)
        return NULL;
      if (may_generate_2STRING(-1, cctx) == FAIL)
        return NULL;
***************
*** 6243,6249 ****
  
      for (;;)
      {
!       if (compile_expr1(&p, cctx) == FAIL)
            return NULL;
        ++count;
        p = skipwhite(p);
--- 6402,6408 ----
  
      for (;;)
      {
!       if (compile_expr0(&p, cctx) == FAIL)
            return NULL;
        ++count;
        p = skipwhite(p);
***************
*** 6305,6311 ****
                ++count;
            }
            p += 2;
!           if (compile_expr1(&p, cctx) == FAIL)
                return NULL;
            may_generate_2STRING(-1, cctx);
            ++count;
--- 6464,6470 ----
                ++count;
            }
            p += 2;
!           if (compile_expr0(&p, cctx) == FAIL)
                return NULL;
            may_generate_2STRING(-1, cctx);
            ++count;
***************
*** 6423,6429 ****
  
            ufunc->uf_def_arg_idx[i] = instr->ga_len;
            arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
!           if (compile_expr1(&arg, &cctx) == FAIL)
                goto erret;
  
            // If no type specified use the type of the default value.
--- 6582,6588 ----
  
            ufunc->uf_def_arg_idx[i] = instr->ga_len;
            arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
!           if (compile_expr0(&arg, &cctx) == FAIL)
                goto erret;
  
            // If no type specified use the type of the default value.
***************
*** 6609,6615 ****
            if (ea.cmdidx == CMD_eval)
            {
                p = ea.cmd;
!               if (compile_expr1(&p, &cctx) == FAIL)
                    goto erret;
  
                // drop the return value
--- 6768,6774 ----
            if (ea.cmdidx == CMD_eval)
            {
                p = ea.cmd;
!               if (compile_expr0(&p, &cctx) == FAIL)
                    goto erret;
  
                // drop the return value
*** ../vim-8.2.0718/src/testdir/test_vim9_disassemble.vim       2020-05-08 
19:10:30.782336716 +0200
--- src/testdir/test_vim9_disassemble.vim       2020-05-09 15:21:35.170848668 
+0200
***************
*** 814,859 ****
  enddef
  
  def Test_disassemble_compare()
-   " TODO: COMPAREFUNC
    let cases = [
!         ['true == false', 'COMPAREBOOL =='],
!         ['true != false', 'COMPAREBOOL !='],
!         ['v:none == v:null', 'COMPARESPECIAL =='],
!         ['v:none != v:null', 'COMPARESPECIAL !='],
! 
!         ['111 == 222', 'COMPARENR =='],
!         ['111 != 222', 'COMPARENR !='],
!         ['111 > 222', 'COMPARENR >'],
!         ['111 < 222', 'COMPARENR <'],
!         ['111 >= 222', 'COMPARENR >='],
!         ['111 <= 222', 'COMPARENR <='],
!         ['111 =~ 222', 'COMPARENR =\~'],
!         ['111 !~ 222', 'COMPARENR !\~'],
! 
!         ['"xx" != "yy"', 'COMPARESTRING !='],
!         ['"xx" > "yy"', 'COMPARESTRING >'],
!         ['"xx" < "yy"', 'COMPARESTRING <'],
!         ['"xx" >= "yy"', 'COMPARESTRING >='],
!         ['"xx" <= "yy"', 'COMPARESTRING <='],
!         ['"xx" =~ "yy"', 'COMPARESTRING =\~'],
!         ['"xx" !~ "yy"', 'COMPARESTRING !\~'],
!         ['"xx" is "yy"', 'COMPARESTRING is'],
!         ['"xx" isnot "yy"', 'COMPARESTRING isnot'],
! 
!         ['0z11 == 0z22', 'COMPAREBLOB =='],
!         ['0z11 != 0z22', 'COMPAREBLOB !='],
!         ['0z11 is 0z22', 'COMPAREBLOB is'],
!         ['0z11 isnot 0z22', 'COMPAREBLOB isnot'],
! 
!         ['[1,2] == [3,4]', 'COMPARELIST =='],
!         ['[1,2] != [3,4]', 'COMPARELIST !='],
!         ['[1,2] is [3,4]', 'COMPARELIST is'],
!         ['[1,2] isnot [3,4]', 'COMPARELIST isnot'],
! 
!         ['#{a:1} == #{x:2}', 'COMPAREDICT =='],
!         ['#{a:1} != #{x:2}', 'COMPAREDICT !='],
!         ['#{a:1} is #{x:2}', 'COMPAREDICT is'],
!         ['#{a:1} isnot #{x:2}', 'COMPAREDICT isnot'],
  
          ['{->33} == {->44}', 'COMPAREFUNC =='],
          ['{->33} != {->44}', 'COMPAREFUNC !='],
--- 814,858 ----
  enddef
  
  def Test_disassemble_compare()
    let cases = [
!         ['true == isFalse', 'COMPAREBOOL =='],
!         ['true != isFalse', 'COMPAREBOOL !='],
!         ['v:none == isNull', 'COMPARESPECIAL =='],
!         ['v:none != isNull', 'COMPARESPECIAL !='],
! 
!         ['111 == aNumber', 'COMPARENR =='],
!         ['111 != aNumber', 'COMPARENR !='],
!         ['111 > aNumber', 'COMPARENR >'],
!         ['111 < aNumber', 'COMPARENR <'],
!         ['111 >= aNumber', 'COMPARENR >='],
!         ['111 <= aNumber', 'COMPARENR <='],
!         ['111 =~ aNumber', 'COMPARENR =\~'],
!         ['111 !~ aNumber', 'COMPARENR !\~'],
! 
!         ['"xx" != aString', 'COMPARESTRING !='],
!         ['"xx" > aString', 'COMPARESTRING >'],
!         ['"xx" < aString', 'COMPARESTRING <'],
!         ['"xx" >= aString', 'COMPARESTRING >='],
!         ['"xx" <= aString', 'COMPARESTRING <='],
!         ['"xx" =~ aString', 'COMPARESTRING =\~'],
!         ['"xx" !~ aString', 'COMPARESTRING !\~'],
!         ['"xx" is aString', 'COMPARESTRING is'],
!         ['"xx" isnot aString', 'COMPARESTRING isnot'],
! 
!         ['0z11 == aBlob', 'COMPAREBLOB =='],
!         ['0z11 != aBlob', 'COMPAREBLOB !='],
!         ['0z11 is aBlob', 'COMPAREBLOB is'],
!         ['0z11 isnot aBlob', 'COMPAREBLOB isnot'],
! 
!         ['[1, 2] == aList', 'COMPARELIST =='],
!         ['[1, 2] != aList', 'COMPARELIST !='],
!         ['[1, 2] is aList', 'COMPARELIST is'],
!         ['[1, 2] isnot aList', 'COMPARELIST isnot'],
! 
!         ['#{a: 1} == aDict', 'COMPAREDICT =='],
!         ['#{a: 1} != aDict', 'COMPAREDICT !='],
!         ['#{a: 1} is aDict', 'COMPAREDICT is'],
!         ['#{a: 1} isnot aDict', 'COMPAREDICT isnot'],
  
          ['{->33} == {->44}', 'COMPAREFUNC =='],
          ['{->33} != {->44}', 'COMPAREFUNC !='],
***************
*** 871,892 ****
          ['77 is g:xx', 'COMPAREANY is'],
          ['77 isnot g:xx', 'COMPAREANY isnot'],
          ]
    if has('float')
      cases->extend([
!         ['1.1 == 2.2', 'COMPAREFLOAT =='],
!         ['1.1 != 2.2', 'COMPAREFLOAT !='],
!         ['1.1 > 2.2', 'COMPAREFLOAT >'],
!         ['1.1 < 2.2', 'COMPAREFLOAT <'],
!         ['1.1 >= 2.2', 'COMPAREFLOAT >='],
!         ['1.1 <= 2.2', 'COMPAREFLOAT <='],
!         ['1.1 =~ 2.2', 'COMPAREFLOAT =\~'],
!         ['1.1 !~ 2.2', 'COMPAREFLOAT !\~'],
          ])
    endif
  
    let nr = 1
    for case in cases
      writefile(['def TestCase' .. nr .. '()',
               '  if ' .. case[0],
               '    echo 42'
               '  endif',
--- 870,902 ----
          ['77 is g:xx', 'COMPAREANY is'],
          ['77 isnot g:xx', 'COMPAREANY isnot'],
          ]
+   let floatDecl = ''
    if has('float')
      cases->extend([
!         ['1.1 == aFloat', 'COMPAREFLOAT =='],
!         ['1.1 != aFloat', 'COMPAREFLOAT !='],
!         ['1.1 > aFloat', 'COMPAREFLOAT >'],
!         ['1.1 < aFloat', 'COMPAREFLOAT <'],
!         ['1.1 >= aFloat', 'COMPAREFLOAT >='],
!         ['1.1 <= aFloat', 'COMPAREFLOAT <='],
!         ['1.1 =~ aFloat', 'COMPAREFLOAT =\~'],
!         ['1.1 !~ aFloat', 'COMPAREFLOAT !\~'],
          ])
+     floatDecl = 'let aFloat = 2.2'
    endif
  
    let nr = 1
    for case in cases
+     " declare local variables to get a non-constant with the right type
      writefile(['def TestCase' .. nr .. '()',
+              '  let isFalse = false',
+              '  let isNull = v:null',
+              '  let aNumber = 222',
+              '  let aString = "yy"',
+              '  let aBlob = 0z22',
+              '  let aList = [3, 4]',
+              '  let aDict = #{x: 2}',
+              floatDecl,
               '  if ' .. case[0],
               '    echo 42'
               '  endif',
***************
*** 896,902 ****
      assert_match('TestCase' .. nr .. '.*' ..
          'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
          '\d \(PUSH\|FUNCREF\).*' ..
!         '\d \(PUSH\|FUNCREF\|LOADG\).*' ..
          '\d ' .. case[1] .. '.*' ..
          '\d JUMP_IF_FALSE -> \d\+.*',
          instr)
--- 906,912 ----
      assert_match('TestCase' .. nr .. '.*' ..
          'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
          '\d \(PUSH\|FUNCREF\).*' ..
!         '\d \(PUSH\|FUNCREF\|LOAD\).*' ..
          '\d ' .. case[1] .. '.*' ..
          '\d JUMP_IF_FALSE -> \d\+.*',
          instr)
*** ../vim-8.2.0718/src/testdir/test_vim9_expr.vim      2020-05-07 
22:18:58.260836097 +0200
--- src/testdir/test_vim9_expr.vim      2020-05-09 15:36:59.004186370 +0200
***************
*** 429,435 ****
  
    call CheckDefFailure(["let x = 1 == '2'"], 'Cannot compare number with 
string')
    call CheckDefFailure(["let x = '1' == 2"], 'Cannot compare string with 
number')
!   call CheckDefFailure(["let x = 1 == RetVoid()"], 'Cannot use void value')
    call CheckDefFailure(["let x = RetVoid() == 1"], 'Cannot compare void with 
number')
  
    call CheckDefFailure(["let x = true > false"], 'Cannot compare bool with 
bool')
--- 429,435 ----
  
    call CheckDefFailure(["let x = 1 == '2'"], 'Cannot compare number with 
string')
    call CheckDefFailure(["let x = '1' == 2"], 'Cannot compare string with 
number')
!   call CheckDefFailure(["let x = 1 == RetVoid()"], 'Cannot compare number 
with void')
    call CheckDefFailure(["let x = RetVoid() == 1"], 'Cannot compare void with 
number')
  
    call CheckDefFailure(["let x = true > false"], 'Cannot compare bool with 
bool')
*** ../vim-8.2.0718/src/version.c       2020-05-09 13:06:20.224712254 +0200
--- src/version.c       2020-05-09 15:07:36.069628668 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     719,
  /**/

-- 
Q: How many legs does a giraffe have?
A: Eight: two in front, two behind, two on the left and two on the right

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
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 on the web visit 
https://groups.google.com/d/msgid/vim_dev/202005091345.049Dj4P9005393%40masaka.moolenaar.net.

Raspunde prin e-mail lui