Patch 8.2.1864
Problem:    Vim9: no error for wrong list type.
Solution:   Add flag to indicate a constant. (closes #7160)
Files:      src/vim9compile.c, src/testdir/test_vim9_assign.vim


*** ../vim-8.2.1863/src/vim9compile.c   2020-10-18 23:32:10.107420617 +0200
--- src/vim9compile.c   2020-10-19 16:00:50.186323298 +0200
***************
*** 815,824 ****
--- 815,847 ----
  }
  
  /*
+  * Return TRUE if "actual" could be "expected" and a runtime typecheck is to 
be
+  * used.  Return FALSE if the types will never match.
+  */
+     static int
+ use_typecheck(type_T *actual, type_T *expected)
+ {
+     if (actual->tt_type == VAR_ANY
+           || actual->tt_type == VAR_UNKNOWN
+           || (actual->tt_type == VAR_FUNC
+               && (expected->tt_type == VAR_FUNC
+                                          || expected->tt_type == VAR_PARTIAL)
+               && (actual->tt_member == &t_any || actual->tt_argcount < 0)))
+       return TRUE;
+     if ((actual->tt_type == VAR_LIST || actual->tt_type == VAR_DICT)
+                                      && actual->tt_type == expected->tt_type)
+       // This takes care of a nested list or dict.
+       return use_typecheck(actual->tt_member, expected->tt_member);
+     return FALSE;
+ }
+ 
+ /*
   * Check that
   * - "actual" matches "expected" type or
   * - "actual" is a type that can be "expected" type: add a runtime check; or
   * - return FAIL.
+  * If "actual_is_const" is TRUE then the type won't change at runtime, do not
+  * generate a TYPECHECK.
   */
      static int
  need_type(
***************
*** 826,832 ****
        type_T  *expected,
        int     offset,
        cctx_T  *cctx,
!       int     silent)
  {
      if (expected == &t_bool && actual != &t_bool
                                        && (actual->tt_flags & TTFLAG_BOOL_OK))
--- 849,856 ----
        type_T  *expected,
        int     offset,
        cctx_T  *cctx,
!       int     silent,
!       int     actual_is_const)
  {
      if (expected == &t_bool && actual != &t_bool
                                        && (actual->tt_flags & TTFLAG_BOOL_OK))
***************
*** 841,859 ****
        return OK;
  
      // If the actual type can be the expected type add a runtime check.
!     // TODO: if it's a constant a runtime check makes no sense.
!     if (actual->tt_type == VAR_ANY
!           || actual->tt_type == VAR_UNKNOWN
!           || (actual->tt_type == VAR_FUNC
!               && (expected->tt_type == VAR_FUNC
!                                          || expected->tt_type == VAR_PARTIAL)
!               && (actual->tt_member == &t_any || actual->tt_argcount < 0))
!           || (actual->tt_type == VAR_LIST
!               && expected->tt_type == VAR_LIST
!               && actual->tt_member == &t_any)
!           || (actual->tt_type == VAR_DICT
!               && expected->tt_type == VAR_DICT
!               && actual->tt_member == &t_any))
      {
        generate_TYPECHECK(cctx, expected, offset);
        return OK;
--- 865,872 ----
        return OK;
  
      // If the actual type can be the expected type add a runtime check.
!     // If it's a constant a runtime check makes no sense.
!     if (!actual_is_const && use_typecheck(actual, expected))
      {
        generate_TYPECHECK(cctx, expected, offset);
        return OK;
***************
*** 1526,1532 ****
            else
                expected = ufunc->uf_va_type->tt_member;
            actual = ((type_T **)stack->ga_data)[stack->ga_len - argcount + i];
!           if (need_type(actual, expected, -argcount + i, cctx, TRUE) == FAIL)
            {
                arg_type_mismatch(expected, actual, i + 1);
                return FAIL;
--- 1539,1546 ----
            else
                expected = ufunc->uf_va_type->tt_member;
            actual = ((type_T **)stack->ga_data)[stack->ga_len - argcount + i];
!           if (need_type(actual, expected, -argcount + i, cctx,
!                                                         TRUE, FALSE) == FAIL)
            {
                arg_type_mismatch(expected, actual, i + 1);
                return FAIL;
***************
*** 2061,2068 ****
--- 2075,2085 ----
  typedef struct {
      typval_T  pp_tv[PPSIZE];  // stack of ppconst constants
      int               pp_used;        // active entries in pp_tv[]
+     int               pp_is_const;    // all generated code was constants, 
used for a
+                               // list or dict with constant members
  } ppconst_T;
  
+ static int compile_expr0_ext(char_u **arg,  cctx_T *cctx, int *is_const);
  static int compile_expr0(char_u **arg,  cctx_T *cctx);
  static int compile_expr1(char_u **arg,  cctx_T *cctx, ppconst_T *ppconst);
  
***************
*** 2629,2641 ****
  /*
   * parse a list: [expr, expr]
   * "*arg" points to the '['.
   */
      static int
! compile_list(char_u **arg, cctx_T *cctx)
  {
      char_u    *p = skipwhite(*arg + 1);
      char_u    *whitep = *arg + 1;
      int               count = 0;
  
      for (;;)
      {
--- 2646,2661 ----
  /*
   * parse a list: [expr, expr]
   * "*arg" points to the '['.
+  * ppconst->pp_is_const is set if all items are a constant.
   */
      static int
! compile_list(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
  {
      char_u    *p = skipwhite(*arg + 1);
      char_u    *whitep = *arg + 1;
      int               count = 0;
+     int               is_const;
+     int               is_all_const = TRUE;    // reset when non-const 
encountered
  
      for (;;)
      {
***************
*** 2654,2661 ****
            ++p;
            break;
        }
!       if (compile_expr0(&p, cctx) == FAIL)
            return FAIL;
        ++count;
        if (*p == ',')
        {
--- 2674,2683 ----
            ++p;
            break;
        }
!       if (compile_expr0_ext(&p, cctx, &is_const) == FAIL)
            return FAIL;
+       if (!is_const)
+           is_all_const = FALSE;
        ++count;
        if (*p == ',')
        {
***************
*** 2671,2678 ****
      }
      *arg = p;
  
!     generate_NEWLIST(cctx, count);
!     return OK;
  }
  
  /*
--- 2693,2700 ----
      }
      *arg = p;
  
!     ppconst->pp_is_const = is_all_const;
!     return generate_NEWLIST(cctx, count);
  }
  
  /*
***************
*** 2772,2780 ****
  /*
   * parse a dict: {'key': val} or #{key: val}
   * "*arg" points to the '{'.
   */
      static int
! compile_dict(char_u **arg, cctx_T *cctx, int literal)
  {
      garray_T  *instr = &cctx->ctx_instr;
      garray_T  *stack = &cctx->ctx_type_stack;
--- 2794,2803 ----
  /*
   * parse a dict: {'key': val} or #{key: val}
   * "*arg" points to the '{'.
+  * ppconst->pp_is_const is set if all item values are a constant.
   */
      static int
! compile_dict(char_u **arg, cctx_T *cctx, int literal, ppconst_T *ppconst)
  {
      garray_T  *instr = &cctx->ctx_instr;
      garray_T  *stack = &cctx->ctx_type_stack;
***************
*** 2783,2788 ****
--- 2806,2813 ----
      dictitem_T        *item;
      char_u    *whitep = *arg;
      char_u    *p;
+     int               is_const;
+     int               is_all_const = TRUE;    // reset when non-const 
encountered
  
      if (d == NULL)
        return FAIL;
***************
*** 2827,2833 ****
            {
                type_T *keytype = ((type_T **)stack->ga_data)
                                                           [stack->ga_len - 1];
!               if (need_type(keytype, &t_string, -1, cctx, FALSE) == FAIL)
                    return FAIL;
            }
        }
--- 2852,2859 ----
            {
                type_T *keytype = ((type_T **)stack->ga_data)
                                                           [stack->ga_len - 1];
!               if (need_type(keytype, &t_string, -1, cctx,
!                                                        FALSE, FALSE) == FAIL)
                    return FAIL;
            }
        }
***************
*** 2873,2880 ****
            goto failret;
        }
  
!       if (compile_expr0(arg, cctx) == FAIL)
            return FAIL;
        ++count;
  
        whitep = *arg;
--- 2899,2908 ----
            goto failret;
        }
  
!       if (compile_expr0_ext(arg, cctx, &is_const) == FAIL)
            return FAIL;
+       if (!is_const)
+           is_all_const = FALSE;
        ++count;
  
        whitep = *arg;
***************
*** 2908,2913 ****
--- 2936,2942 ----
        *arg += STRLEN(*arg);
  
      dict_unref(d);
+     ppconst->pp_is_const = is_all_const;
      return generate_NEWDICT(cctx, count);
  
  failret:
***************
*** 3245,3250 ****
--- 3274,3280 ----
  
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
+           ppconst->pp_is_const = FALSE;
  
            // funcref(arg)
            type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
***************
*** 3261,3266 ****
--- 3291,3297 ----
  
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
+           ppconst->pp_is_const = FALSE;
  
            // something->method()
            // Apply the '!', '-' and '+' first:
***************
*** 3316,3321 ****
--- 3347,3353 ----
            // TODO: recognize list or dict at runtime
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
+           ppconst->pp_is_const = FALSE;
  
            ++p;
            *arg = skipwhite(p);
***************
*** 3371,3382 ****
                vtype = VAR_DICT;
            if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
            {
!               if (need_type(valtype, &t_number, -1, cctx, FALSE) == FAIL)
                    return FAIL;
                if (is_slice)
                {
                    valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
!                   if (need_type(valtype, &t_number, -2, cctx, FALSE) == FAIL)
                        return FAIL;
                }
            }
--- 3403,3416 ----
                vtype = VAR_DICT;
            if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
            {
!               if (need_type(valtype, &t_number, -1, cctx,
!                                                        FALSE, FALSE) == FAIL)
                    return FAIL;
                if (is_slice)
                {
                    valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
!                   if (need_type(valtype, &t_number, -2, cctx,
!                                                        FALSE, FALSE) == FAIL)
                        return FAIL;
                }
            }
***************
*** 3392,3398 ****
                    *typep = (*typep)->tt_member;
                else
                {
!                   if (need_type(*typep, &t_dict_any, -2, cctx, FALSE) == FAIL)
                        return FAIL;
                    *typep = &t_any;
                }
--- 3426,3433 ----
                    *typep = (*typep)->tt_member;
                else
                {
!                   if (need_type(*typep, &t_dict_any, -2, cctx,
!                                                        FALSE, FALSE) == FAIL)
                        return FAIL;
                    *typep = &t_any;
                }
***************
*** 3441,3448 ****
--- 3476,3485 ----
        }
        else if (*p == '.' && p[1] != '.')
        {
+           // dictionary member: dict.name
            if (generate_ppconst(cctx, ppconst) == FAIL)
                return FAIL;
+           ppconst->pp_is_const = FALSE;
  
            *arg = p + 1;
            if (may_get_next_line(*arg, arg, cctx) == FAIL)
***************
*** 3450,3456 ****
                emsg(_(e_missing_name_after_dot));
                return FAIL;
            }
-           // dictionary member: dict.name
            p = *arg;
            if (eval_isdictc(*p))
                while (eval_isnamec(*p))
--- 3487,3492 ----
***************
*** 3480,3486 ****
   * Compile an expression at "*arg" and add instructions to "cctx->ctx_instr".
   * "arg" is advanced until after the expression, skipping white space.
   *
!  * If the value is a constant "ppconst->pp_ret" will be set.
   * Before instructions are generated, any values in "ppconst" will generated.
   *
   * This is the compiling equivalent of eval1(), eval2(), etc.
--- 3516,3522 ----
   * Compile an expression at "*arg" and add instructions to "cctx->ctx_instr".
   * "arg" is advanced until after the expression, skipping white space.
   *
!  * If the value is a constant "ppconst->pp_used" will be non-zero.
   * Before instructions are generated, any values in "ppconst" will generated.
   *
   * This is the compiling equivalent of eval1(), eval2(), etc.
***************
*** 3521,3526 ****
--- 3557,3564 ----
      typval_T  *rettv = &ppconst->pp_tv[ppconst->pp_used];
      int               used_before = ppconst->pp_used;
  
+     ppconst->pp_is_const = FALSE;
+ 
      /*
       * Skip '!', '-' and '+' characters.  They are handled later.
       */
***************
*** 3610,3616 ****
        /*
         * List: [expr, expr]
         */
!       case '[':   ret = compile_list(arg, cctx);
                    break;
  
        /*
--- 3648,3654 ----
        /*
         * List: [expr, expr]
         */
!       case '[':   ret = compile_list(arg, cctx, ppconst);
                    break;
  
        /*
***************
*** 3619,3625 ****
        case '#':   if ((*arg)[1] == '{')
                    {
                        ++*arg;
!                       ret = compile_dict(arg, cctx, TRUE);
                    }
                    else
                        ret = NOTDONE;
--- 3657,3663 ----
        case '#':   if ((*arg)[1] == '{')
                    {
                        ++*arg;
!                       ret = compile_dict(arg, cctx, TRUE, ppconst);
                    }
                    else
                        ret = NOTDONE;
***************
*** 3638,3644 ****
                        if (ret != FAIL && *start == '>')
                            ret = compile_lambda(arg, cctx);
                        else
!                           ret = compile_dict(arg, cctx, FALSE);
                    }
                    break;
  
--- 3676,3682 ----
                        if (ret != FAIL && *start == '>')
                            ret = compile_lambda(arg, cctx);
                        else
!                           ret = compile_dict(arg, cctx, FALSE, ppconst);
                    }
                    break;
  
***************
*** 3807,3813 ****
        actual = ((type_T **)stack->ga_data)[stack->ga_len - 1];
        if (check_type(want_type, actual, FALSE, 0) == FAIL)
        {
!           if (need_type(actual, want_type, -1, cctx, FALSE) == FAIL)
                return FAIL;
        }
      }
--- 3845,3851 ----
        actual = ((type_T **)stack->ga_data)[stack->ga_len - 1];
        if (check_type(want_type, actual, FALSE, 0) == FAIL)
        {
!           if (need_type(actual, want_type, -1, cctx, FALSE, FALSE) == FAIL)
                return FAIL;
        }
      }
***************
*** 4420,4428 ****
  
  /*
   * Toplevel expression.
   */
      static int
! compile_expr0(char_u **arg,  cctx_T *cctx)
  {
      ppconst_T ppconst;
  
--- 4458,4468 ----
  
  /*
   * Toplevel expression.
+  * Sets "is_const" (if not NULL) to indicate the value is a constant.
+  * Returns OK or FAIL.
   */
      static int
! compile_expr0_ext(char_u **arg,  cctx_T *cctx, int *is_const)
  {
      ppconst_T ppconst;
  
***************
*** 4432,4443 ****
--- 4472,4494 ----
        clear_ppconst(&ppconst);
        return FAIL;
      }
+     if (is_const != NULL)
+       *is_const = ppconst.pp_used > 0 || ppconst.pp_is_const;
      if (generate_ppconst(cctx, &ppconst) == FAIL)
        return FAIL;
      return OK;
  }
  
  /*
+  * Toplevel expression.
+  */
+     static int
+ compile_expr0(char_u **arg,  cctx_T *cctx)
+ {
+     return compile_expr0_ext(arg, cctx, NULL);
+ }
+ 
+ /*
   * compile "return [expr]"
   */
      static char_u *
***************
*** 4466,4472 ****
                return NULL;
            }
            if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1,
!                                                         cctx, FALSE) == FAIL)
                return NULL;
        }
      }
--- 4517,4523 ----
                return NULL;
            }
            if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1,
!                                                  cctx, FALSE, FALSE) == FAIL)
                return NULL;
        }
      }
***************
*** 4834,4840 ****
                emsg(_(e_cannot_use_void_value));
                goto theend;
            }
!           if (need_type(stacktype, &t_list_any, -1, cctx, FALSE) == FAIL)
                goto theend;
            // TODO: check the length of a constant list here
            generate_CHECKLEN(cctx, semicolon ? var_count - 1 : var_count,
--- 4885,4892 ----
                emsg(_(e_cannot_use_void_value));
                goto theend;
            }
!           if (need_type(stacktype, &t_list_any, -1, cctx,
!                                                        FALSE, FALSE) == FAIL)
                goto theend;
            // TODO: check the length of a constant list here
            generate_CHECKLEN(cctx, semicolon ? var_count - 1 : var_count,
***************
*** 5194,5199 ****
--- 5246,5252 ----
            else if (oplen > 0)
            {
                type_T  *stacktype;
+               int     is_const = FALSE;
  
                // For "var = expr" evaluate the expression.
                if (var_count == 0)
***************
*** 5219,5225 ****
                        --cctx->ctx_locals.ga_len;
                    instr_count = instr->ga_len;
                    p = skipwhite(op + oplen);
!                   r = compile_expr0(&p, cctx);
                    if (new_local)
                        ++cctx->ctx_locals.ga_len;
                    if (r == FAIL)
--- 5272,5278 ----
                        --cctx->ctx_locals.ga_len;
                    instr_count = instr->ga_len;
                    p = skipwhite(op + oplen);
!                   r = compile_expr0_ext(&p, cctx, &is_const);
                    if (new_local)
                        ++cctx->ctx_locals.ga_len;
                    if (r == FAIL)
***************
*** 5281,5293 ****
                                // could be indexing "any"
                                use_type = &t_any;
                        }
!                       if (need_type(stacktype, use_type, -1, cctx, FALSE)
!                                                                      == FAIL)
                            goto theend;
                    }
                }
                else if (*p != '=' && need_type(stacktype, member_type, -1,
!                                                         cctx, FALSE) == FAIL)
                    goto theend;
            }
            else if (cmdidx == CMD_final)
--- 5334,5346 ----
                                // could be indexing "any"
                                use_type = &t_any;
                        }
!                       if (need_type(stacktype, use_type, -1, cctx,
!                                                     FALSE, is_const) == FAIL)
                            goto theend;
                    }
                }
                else if (*p != '=' && need_type(stacktype, member_type, -1,
!                                                  cctx, FALSE, FALSE) == FAIL)
                    goto theend;
            }
            else if (cmdidx == CMD_final)
***************
*** 5374,5380 ****
                // If variable is float operation with number is OK.
                !(expected == &t_float && stacktype == &t_number) &&
  #endif
!                   need_type(stacktype, expected, -1, cctx, FALSE) == FAIL)
                goto theend;
  
            if (*op == '.')
--- 5427,5434 ----
                // If variable is float operation with number is OK.
                !(expected == &t_float && stacktype == &t_number) &&
  #endif
!                   need_type(stacktype, expected, -1, cctx,
!                                                        FALSE, FALSE) == FAIL)
                goto theend;
  
            if (*op == '.')
***************
*** 5768,5774 ****
  
      type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
      if (type != &t_bool && type != &t_number && type != &t_any
!           && need_type(type, &t_bool, -1, cctx, FALSE) == FAIL)
        return FAIL;
      return OK;
  }
--- 5822,5828 ----
  
      type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
      if (type != &t_bool && type != &t_number && type != &t_any
!           && need_type(type, &t_bool, -1, cctx, FALSE, FALSE) == FAIL)
        return FAIL;
      return OK;
  }
***************
*** 6105,6111 ****
      // Now that we know the type of "var", check that it is a list, now or at
      // runtime.
      vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
!     if (need_type(vartype, &t_list_any, -1, cctx, FALSE) == FAIL)
      {
        drop_scope(cctx);
        return NULL;
--- 6159,6165 ----
      // Now that we know the type of "var", check that it is a list, now or at
      // runtime.
      vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
!     if (need_type(vartype, &t_list_any, -1, cctx, FALSE, FALSE) == FAIL)
      {
        drop_scope(cctx);
        return NULL;
*** ../vim-8.2.1863/src/testdir/test_vim9_assign.vim    2020-10-17 
22:04:04.118833463 +0200
--- src/testdir/test_vim9_assign.vim    2020-10-19 16:03:10.341906066 +0200
***************
*** 702,707 ****
--- 702,710 ----
      nrl[i] = i
    endfor
    assert_equal([0, 1, 2, 3, 4], nrl)
+ 
+   CheckDefFailure(["var l: list<number> = ['', true]"], 'E1012: Type 
mismatch; expected list<number> but got list<any>', 1)
+   CheckDefFailure(["var l: list<list<number>> = [['', true]]"], 'E1012: Type 
mismatch; expected list<list<number>> but got list<list<any>>', 1)
  enddef
  
  def Test_assign_dict()
***************
*** 718,723 ****
--- 721,729 ----
      nrd[i] = i
    endfor
    assert_equal({'0': 0, '1': 1, '2': 2}, nrd)
+ 
+   CheckDefFailure(["var d: dict<number> = #{a: '', b: true}"], 'E1012: Type 
mismatch; expected dict<number> but got dict<any>', 1)
+   CheckDefFailure(["var d: dict<dict<number>> = #{x: #{a: '', b: true}}"], 
'E1012: Type mismatch; expected dict<dict<number>> but got dict<dict<any>>', 1)
  enddef
  
  def Test_assign_dict_unknown_type()
*** ../vim-8.2.1863/src/version.c       2020-10-19 13:12:29.844428375 +0200
--- src/version.c       2020-10-19 14:49:56.614641391 +0200
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     1864,
  /**/

-- 
I AM THANKFUL...
...for the taxes that I pay because it means that I am employed.

 /// 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/202010191408.09JE88jG1150398%40masaka.moolenaar.net.

Raspunde prin e-mail lui