Patch 9.0.0623
Problem:    Error for modifying a const is not detected at compile time.
Solution:   Add TTFLAG_CONST and check for it in add() and extend().
Files:      src/structs.h, src/globals.h, src/vim9compile.c, src/vim9type.c,
            src/proto/vim9type.pro, src/errors.h, src/evalfunc.c,
            src/proto/evalfunc.pro, src/vim9instr.c,
            src/testdir/test_vim9_builtin.vim,
            src/testdir/test_vim9_script.vim


*** ../vim-9.0.0622/src/structs.h       2022-09-29 12:49:52.257601397 +0100
--- src/structs.h       2022-09-29 15:42:38.141330062 +0100
***************
*** 1445,1454 ****
      type_T    *type_decl;         // declared type or equal to type_current
  } type2_T;
  
! #define TTFLAG_VARARGS        1           // func args ends with "..."
! #define TTFLAG_OPTARG 2           // func arg type with "?"
! #define TTFLAG_BOOL_OK        4           // can be converted to bool
! #define TTFLAG_STATIC 8           // one of the static types, e.g. t_any
  
  /*
   * Structure to hold an internal variable without a name.
--- 1445,1454 ----
      type_T    *type_decl;         // declared type or equal to type_current
  } type2_T;
  
! #define TTFLAG_VARARGS        0x01        // func args ends with "..."
! #define TTFLAG_BOOL_OK        0x02        // can be converted to bool
! #define TTFLAG_STATIC 0x04        // one of the static types, e.g. t_any
! #define TTFLAG_CONST  0x08        // cannot be changed
  
  /*
   * Structure to hold an internal variable without a name.
*** ../vim-9.0.0622/src/globals.h       2022-09-19 16:45:26.202239861 +0100
--- src/globals.h       2022-09-29 17:15:08.440503389 +0100
***************
*** 396,460 ****
  EXTERN int    garbage_collect_at_exit INIT(= FALSE);
  
  
! // Commonly used types.
! // "unknown" is used for when the type is really unknown, e.g. global
! // variables.  Also for when a function may or may not return something.
! EXTERN type_T t_unknown INIT6(VAR_UNKNOWN, 0, 0, TTFLAG_STATIC, NULL, NULL);
! 
! // "any" is used for when the type is mixed.  Excludes "void".
! EXTERN type_T t_any INIT6(VAR_ANY, 0, 0, TTFLAG_STATIC, NULL, NULL);
! 
! // "void" is used for a function not returning anything.
! EXTERN type_T t_void INIT6(VAR_VOID, 0, 0, TTFLAG_STATIC, NULL, NULL);
! 
! EXTERN type_T t_bool INIT6(VAR_BOOL, 0, 0, TTFLAG_STATIC, NULL, NULL);
! EXTERN type_T t_null INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL);
! EXTERN type_T t_none INIT6(VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL);
! EXTERN type_T t_number INIT6(VAR_NUMBER, 0, 0, TTFLAG_STATIC, NULL, NULL);
! EXTERN type_T t_number_bool INIT6(VAR_NUMBER, 0, 0, 
TTFLAG_STATIC|TTFLAG_BOOL_OK, NULL, NULL);
! EXTERN type_T t_float INIT6(VAR_FLOAT, 0, 0, TTFLAG_STATIC, NULL, NULL);
! EXTERN type_T t_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL);
! EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
! EXTERN type_T t_blob_null INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, &t_void, NULL);
! EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL);
! EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL);
! 
! // Special value used for @#.
! EXTERN type_T t_number_or_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, 
NULL);
! 
! EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, -1, TTFLAG_STATIC, 
&t_unknown, NULL);
! EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_void, 
NULL);
! EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_any, NULL);
! EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_number, 
NULL);
! EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_string, 
NULL);
! EXTERN type_T t_func_bool INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_bool, 
NULL);
! EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_void, 
NULL);
! EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_any, NULL);
! EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_number, 
NULL);
! EXTERN type_T t_func_0_string INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_string, 
NULL);
! 
! EXTERN type_T t_list_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_any, NULL);
! EXTERN type_T t_dict_any INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_any, NULL);
! EXTERN type_T t_list_empty INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_unknown, 
NULL);
! EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_unknown, 
NULL);
! 
! EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_bool, NULL);
! EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_number, 
NULL);
! EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_string, 
NULL);
! EXTERN type_T t_list_job INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_job, NULL);
! EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, 
&t_dict_any, NULL);
! EXTERN type_T t_list_list_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, 
&t_list_any, NULL);
! EXTERN type_T t_list_list_string INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, 
&t_list_string, NULL);
! 
! EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_bool, NULL);
! EXTERN type_T t_dict_number INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_number, 
NULL);
! EXTERN type_T t_dict_string INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_string, 
NULL);
  
  #endif
  
- #ifdef FEAT_EVAL
  EXTERN int    did_source_packages INIT(= FALSE);
! #endif
  
  // Magic number used for hashitem "hi_key" value indicating a deleted item.
  // Only the address is used.
--- 396,696 ----
  EXTERN int    garbage_collect_at_exit INIT(= FALSE);
  
  
! // Array with predefined commonly used types.
! //
! // For each entry of a regular type the next one has the "const" version.
! // E.g. "&t_const_bool == &t_bool + 1"
  
+ // t_unknown - used for when the type is really unknown, e.g. global 
variables.
+ // Also for when a function may or may not return something.
+ #define t_unknown             (static_types[0])
+ #define t_const_unknown               (static_types[1])
+ 
+ // t_any -  used for when the type can be anything, but excludes "void".
+ #define t_any                 (static_types[2])
+ #define t_const_any           (static_types[3])
+ 
+ // t_void - used for a function not returning anything.
+ #define t_void                        (static_types[4])
+ #define t_const_void          (static_types[5])
+ 
+ #define t_bool                        (static_types[6])
+ #define t_const_bool          (static_types[7])
+ 
+ #define t_null                        (static_types[8])
+ #define t_const_null          (static_types[9])
+ 
+ #define t_none                        (static_types[10])
+ #define t_const_none          (static_types[11])
+ 
+ #define t_number              (static_types[12])
+ #define t_const_number                (static_types[13])
+ 
+ // t_number_bool - number that can be used as a bool
+ #define t_number_bool         (static_types[14])
+ #define t_const_number_bool   (static_types[15])
+ 
+ #define t_float                       (static_types[16])
+ #define t_const_float         (static_types[17])
+ 
+ #define t_string              (static_types[18])
+ #define t_const_string                (static_types[19])
+ 
+ #define t_blob                        (static_types[20])
+ #define t_const_blob          (static_types[21])
+ 
+ #define t_blob_null           (static_types[22])
+ #define t_const_blob_null     (static_types[23])
+ 
+ #define t_job                 (static_types[24])
+ #define t_const_job           (static_types[25])
+ 
+ #define t_channel             (static_types[26])
+ #define t_const_channel               (static_types[27])
+ 
+ // t_number_or_string - Special value used for @#.
+ #define t_number_or_string    (static_types[28])
+ #define t_const_number_or_string (static_types[29])
+ 
+ // t_func_unknown - function with any arguments and no or unknown return value
+ #define t_func_unknown                (static_types[30])
+ #define t_const_func_unknown  (static_types[31])
+ 
+ // t_func_void - function with any arguments and no return value
+ #define t_func_void           (static_types[32])
+ #define t_const_func_void     (static_types[33])
+ 
+ #define t_func_any            (static_types[34])
+ #define t_const_func_any      (static_types[35])
+ 
+ #define t_func_number         (static_types[36])
+ #define t_const_func_number   (static_types[37])
+ 
+ #define t_func_string         (static_types[38])
+ #define t_const_func_string   (static_types[39])
+ 
+ #define t_func_bool           (static_types[40])
+ #define t_const_func_bool     (static_types[41])
+ 
+ // t_func_0_void - function without arguments and nor return value
+ #define t_func_0_void         (static_types[42])
+ #define t_const_func_0_void   (static_types[43])
+ 
+ #define t_func_0_any          (static_types[44])
+ #define t_const_func_0_any    (static_types[45])
+ 
+ #define t_func_0_number               (static_types[46])
+ #define t_const_func_0_number (static_types[47])
+ 
+ #define t_func_0_string               (static_types[48])
+ #define t_const_func_0_string (static_types[49])
+ 
+ #define t_list_any            (static_types[50])
+ #define t_const_list_any      (static_types[51])
+ 
+ #define t_dict_any            (static_types[52])
+ #define t_const_dict_any      (static_types[53])
+ 
+ #define t_list_empty          (static_types[54])
+ #define t_const_list_empty    (static_types[55])
+ 
+ #define t_dict_empty          (static_types[56])
+ #define t_const_dict_empty    (static_types[57])
+ 
+ #define t_list_bool           (static_types[58])
+ #define t_const_list_bool     (static_types[59])
+ 
+ #define t_list_number         (static_types[60])
+ #define t_const_list_number   (static_types[61])
+ 
+ #define t_list_string         (static_types[62])
+ #define t_const_list_string   (static_types[63])
+ 
+ #define t_list_job            (static_types[64])
+ #define t_const_list_job      (static_types[65])
+ 
+ #define t_list_dict_any               (static_types[66])
+ #define t_const_list_dict_any (static_types[67])
+ 
+ #define t_list_list_any               (static_types[68])
+ #define t_const_list_list_any (static_types[69])
+ 
+ #define t_list_list_string    (static_types[70])
+ #define t_const_list_list_string (static_types[71])
+ 
+ #define t_dict_bool           (static_types[72])
+ #define t_const_dict_bool     (static_types[73])
+ 
+ #define t_dict_number         (static_types[74])
+ #define t_const_dict_number   (static_types[75])
+ 
+ #define t_dict_string         (static_types[76])
+ #define t_const_dict_string   (static_types[77])
+ 
+ EXTERN type_T static_types[78]
+ #ifdef DO_INIT
+ = {
+     // 0: t_unknown
+     {VAR_UNKNOWN, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_UNKNOWN, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 2: t_any
+     {VAR_ANY, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_ANY, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 4: t_void
+     {VAR_VOID, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_VOID, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 6: t_bool
+     {VAR_BOOL, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_BOOL, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 8: t_null
+     {VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_SPECIAL, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 10: t_none
+     {VAR_SPECIAL, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_SPECIAL, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 12: t_number
+     {VAR_NUMBER, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_NUMBER, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 14: t_number_bool
+     {VAR_NUMBER, 0, 0, TTFLAG_STATIC|TTFLAG_BOOL_OK, NULL, NULL},
+     {VAR_NUMBER, 0, 0, TTFLAG_STATIC|TTFLAG_BOOL_OK|TTFLAG_CONST, NULL, NULL},
+ 
+     // 16: t_float
+     {VAR_FLOAT, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_FLOAT, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 18: t_string
+     {VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_STRING, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 20: t_blob
+     {VAR_BLOB, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_BLOB, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 22: t_blob_null
+     {VAR_BLOB, 0, 0, TTFLAG_STATIC, &t_void, NULL},
+     {VAR_BLOB, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_void, NULL},
+ 
+     // 24: t_job
+     {VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_JOB, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 26: t_channel
+     {VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_CHANNEL, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 28: t_number_or_string
+     {VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL},
+     {VAR_STRING, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, NULL, NULL},
+ 
+     // 30: t_func_unknown
+     {VAR_FUNC, -1, -1, TTFLAG_STATIC, &t_unknown, NULL},
+     {VAR_FUNC, -1, -1, TTFLAG_STATIC|TTFLAG_CONST, &t_unknown, NULL},
+ 
+     // 32: t_func_void
+     {VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_void, NULL},
+     {VAR_FUNC, -1, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_void, NULL},
+ 
+     // 34: t_func_any
+     {VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_any, NULL},
+     {VAR_FUNC, -1, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_any, NULL},
+ 
+     // 36: t_func_number
+     {VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_number, NULL},
+     {VAR_FUNC, -1, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_number, NULL},
+ 
+     // 38: t_func_string
+     {VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_string, NULL},
+     {VAR_FUNC, -1, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_string, NULL},
+ 
+     // 40: t_func_bool
+     {VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_bool, NULL},
+     {VAR_FUNC, -1, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_bool, NULL},
+ 
+     // 42: t_func_0_void
+     {VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_void, NULL},
+     {VAR_FUNC, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_void, NULL},
+ 
+     // 44: t_func_0_any
+     {VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_any, NULL},
+     {VAR_FUNC, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_any, NULL},
+ 
+     // 46: t_func_0_number
+     {VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_number, NULL},
+     {VAR_FUNC, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_number, NULL},
+ 
+     // 48: t_func_0_string
+     {VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_string, NULL},
+     {VAR_FUNC, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_string, NULL},
+ 
+     // 50: t_list_any
+     {VAR_LIST, 0, 0, TTFLAG_STATIC, &t_any, NULL},
+     {VAR_LIST, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_any, NULL},
+ 
+     // 52: t_dict_any
+     {VAR_DICT, 0, 0, TTFLAG_STATIC, &t_any, NULL},
+     {VAR_DICT, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_any, NULL},
+ 
+     // 54: t_list_empty
+     {VAR_LIST, 0, 0, TTFLAG_STATIC, &t_unknown, NULL},
+     {VAR_LIST, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_unknown, NULL},
+ 
+     // 56: t_dict_empty
+     {VAR_DICT, 0, 0, TTFLAG_STATIC, &t_unknown, NULL},
+     {VAR_DICT, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_unknown, NULL},
+ 
+     // 58: t_list_bool
+     {VAR_LIST, 0, 0, TTFLAG_STATIC, &t_bool, NULL},
+     {VAR_LIST, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_bool, NULL},
+ 
+     // 60: t_list_number
+     {VAR_LIST, 0, 0, TTFLAG_STATIC, &t_number, NULL},
+     {VAR_LIST, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_number, NULL},
+ 
+     // 62: t_list_string
+     {VAR_LIST, 0, 0, TTFLAG_STATIC, &t_string, NULL},
+     {VAR_LIST, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_string, NULL},
+ 
+     // 64: t_list_job
+     {VAR_LIST, 0, 0, TTFLAG_STATIC, &t_job, NULL},
+     {VAR_LIST, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_job, NULL},
+ 
+     // 66: t_list_dict_any
+     {VAR_LIST, 0, 0, TTFLAG_STATIC, &t_dict_any, NULL},
+     {VAR_LIST, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_dict_any, NULL},
+ 
+     // 68: t_list_list_any
+     {VAR_LIST, 0, 0, TTFLAG_STATIC, &t_list_any, NULL},
+     {VAR_LIST, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_list_any, NULL},
+ 
+     // 70: t_list_list_string
+     {VAR_LIST, 0, 0, TTFLAG_STATIC, &t_list_string, NULL},
+     {VAR_LIST, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_list_string, NULL},
+ 
+     // 72: t_dict_bool
+     {VAR_DICT, 0, 0, TTFLAG_STATIC, &t_bool, NULL},
+     {VAR_DICT, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_bool, NULL},
+ 
+     // 74: t_dict_number
+     {VAR_DICT, 0, 0, TTFLAG_STATIC, &t_number, NULL},
+     {VAR_DICT, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_number, NULL},
+ 
+     // 76: t_dict_string
+     {VAR_DICT, 0, 0, TTFLAG_STATIC, &t_string, NULL},
+     {VAR_DICT, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_string, NULL},
+ }
  #endif
+ ;
  
  EXTERN int    did_source_packages INIT(= FALSE);
! #endif // FEAT_EVAL
  
  // Magic number used for hashitem "hi_key" value indicating a deleted item.
  // Only the address is used.
*** ../vim-9.0.0622/src/vim9compile.c   2022-09-19 16:02:40.122484610 +0100
--- src/vim9compile.c   2022-09-29 18:11:25.753937871 +0100
***************
*** 463,468 ****
--- 463,491 ----
  }
  
  /*
+  * Set type of variable "lvar" to "type".  If the variable is a constant then
+  * the type gets TTFLAG_CONST.
+  */
+     static void
+ set_var_type(lvar_T *lvar, type_T *type_arg, cctx_T *cctx)
+ {
+     type_T    *type = type_arg;
+ 
+     if (lvar->lv_const && (type->tt_flags & TTFLAG_CONST) == 0)
+     {
+       if (type->tt_flags & TTFLAG_STATIC)
+           // entry in static_types[] is followed by const type
+           type = type + 1;
+       else
+       {
+           type = copy_type(type, cctx->ctx_type_list);
+           type->tt_flags |= TTFLAG_CONST;
+       }
+     }
+     lvar->lv_type = type;
+ }
+ 
+ /*
   * Reserve space for a local variable.
   * Return the variable or NULL if it failed.
   */
***************
*** 497,503 ****
  
      lvar->lv_name = vim_strnsave(name, len == 0 ? STRLEN(name) : len);
      lvar->lv_const = isConst;
!     lvar->lv_type = type;
  
      // Remember the name for debugging.
      if (GA_GROW_FAILS(&dfunc->df_var_names, 1))
--- 520,531 ----
  
      lvar->lv_name = vim_strnsave(name, len == 0 ? STRLEN(name) : len);
      lvar->lv_const = isConst;
!     if (type == &t_unknown || type == &t_any)
!       // type not known yet, may be inferred from RHS
!       lvar->lv_type = type;
!     else
!       // may use TTFLAG_CONST
!       set_var_type(lvar, type, cctx);
  
      // Remember the name for debugging.
      if (GA_GROW_FAILS(&dfunc->df_var_names, 1))
***************
*** 2304,2322 ****
                        }
                        else
                        {
                            // An empty list or dict has a &t_unknown member,
                            // for a variable that implies &t_any.
                            if (rhs_type == &t_list_empty)
!                               lhs.lhs_lvar->lv_type = &t_list_any;
                            else if (rhs_type == &t_dict_empty)
!                               lhs.lhs_lvar->lv_type = &t_dict_any;
                            else if (rhs_type == &t_unknown)
!                               lhs.lhs_lvar->lv_type = &t_any;
                            else
                            {
!                               lhs.lhs_lvar->lv_type = rhs_type;
                                inferred_type = rhs_type;
                            }
                        }
                    }
                    else if (*op == '=')
--- 2332,2353 ----
                        }
                        else
                        {
+                           type_T *type;
+ 
                            // An empty list or dict has a &t_unknown member,
                            // for a variable that implies &t_any.
                            if (rhs_type == &t_list_empty)
!                               type = &t_list_any;
                            else if (rhs_type == &t_dict_empty)
!                               type = &t_dict_any;
                            else if (rhs_type == &t_unknown)
!                               type = &t_any;
                            else
                            {
!                               type = rhs_type;
                                inferred_type = rhs_type;
                            }
+                           set_var_type(lhs.lhs_lvar, type, cctx);
                        }
                    }
                    else if (*op == '=')
*** ../vim-9.0.0622/src/vim9type.c      2022-09-17 21:07:52.103993150 +0100
--- src/vim9type.c      2022-09-29 16:18:28.354119118 +0100
***************
*** 45,50 ****
--- 45,74 ----
      return type;
  }
  
+ /*
+  * Make a shallow copy of "type".
+  * When allocation fails returns "type".
+  */
+     type_T *
+ copy_type(type_T *type, garray_T *type_gap)
+ {
+     type_T *copy = get_type_ptr(type_gap);
+ 
+     if (copy == NULL)
+       return type;
+     *copy = *type;
+ 
+     if (type->tt_args != NULL)
+     {
+       copy->tt_args = ALLOC_MULT(type_T *, type->tt_argcount);
+       if (copy->tt_args != NULL)
+           for (int i = 0; i < type->tt_argcount; ++i)
+               copy->tt_args[i] = type->tt_args[i];
+     }
+ 
+     return copy;
+ }
+ 
      void
  clear_type_list(garray_T *gap)
  {
*** ../vim-9.0.0622/src/proto/vim9type.pro      2022-06-27 23:15:31.000000000 
+0100
--- src/proto/vim9type.pro      2022-09-29 16:21:55.866041626 +0100
***************
*** 1,4 ****
--- 1,5 ----
  /* vim9type.c */
+ type_T *copy_type(type_T *type, garray_T *type_gap);
  void clear_type_list(garray_T *gap);
  type_T *alloc_type(type_T *type);
  void free_type(type_T *type);
*** ../vim-9.0.0622/src/errors.h        2022-09-20 11:49:06.007787298 +0100
--- src/errors.h        2022-09-29 18:31:07.433537276 +0100
***************
*** 3332,3335 ****
--- 3332,3337 ----
  #ifdef FEAT_EVAL
  EXTERN char e_loop_nesting_too_deep[]
        INIT(= N_("E1306: Loop nesting too deep"));
+ EXTERN char e_argument_nr_trying_to_modify_const_str[]
+       INIT(= N_("E1307: Argument %d: Trying to modify a const %s"));
  #endif
*** ../vim-9.0.0622/src/evalfunc.c      2022-09-28 16:16:10.256335629 +0100
--- src/evalfunc.c      2022-09-29 18:48:11.316704571 +0100
***************
*** 222,227 ****
--- 222,243 ----
  }
  
  /*
+  * Give an error if "type" is a constant.
+  */
+     int
+ arg_type_modifiable(type_T *type, int arg_idx)
+ {
+     char *tofree;
+ 
+     if ((type->tt_flags & TTFLAG_CONST) == 0)
+       return OK;
+     semsg(_(e_argument_nr_trying_to_modify_const_str),
+           arg_idx, type_name(type, &tofree));
+     vim_free(tofree);
+     return FAIL;
+ }
+ 
+ /*
   * Check "type" is a float or a number.
   */
      static int
***************
*** 324,329 ****
--- 340,359 ----
  }
  
  /*
+  * Check "type" is a modifiable list of 'any' or a blob.
+  */
+     static int
+ arg_list_or_blob_mod(
+       type_T       *type,
+       type_T       *decl_type,
+       argcontext_T *context)
+ {
+     if (arg_list_or_blob(type, decl_type, context) == FAIL)
+       return FAIL;
+     return arg_type_modifiable(type, context->arg_idx + 1);
+ }
+ 
+ /*
   * Check "type" is a string or a number
   */
      static int
***************
*** 468,473 ****
--- 498,517 ----
  }
  
  /*
+  * Check "type" is a list of 'any' or a dict of 'any'.  And modifiable.
+  */
+     static int
+ arg_list_or_dict_mod(
+       type_T       *type,
+       type_T       *decl_type,
+       argcontext_T *context)
+ {
+     if (arg_list_or_dict(type, decl_type, context) == FAIL)
+       return FAIL;
+     return arg_type_modifiable(type, context->arg_idx + 1);
+ }
+ 
+ /*
   * Check "type" is a list of 'any' or a dict of 'any' or a blob.
   */
      static int
***************
*** 979,985 ****
  static argcheck_T arg2_list_any_string[] = {arg_list_any, arg_string};
  static argcheck_T arg2_list_number[] = {arg_list_number, arg_list_number};
  static argcheck_T arg2_list_number_bool[] = {arg_list_number, arg_bool};
! static argcheck_T arg2_listblob_item[] = {arg_list_or_blob, arg_item_of_prev};
  static argcheck_T arg2_lnum[] = {arg_lnum, arg_lnum};
  static argcheck_T arg2_lnum_number[] = {arg_lnum, arg_number};
  static argcheck_T arg2_number[] = {arg_number, arg_number};
--- 1023,1029 ----
  static argcheck_T arg2_list_any_string[] = {arg_list_any, arg_string};
  static argcheck_T arg2_list_number[] = {arg_list_number, arg_list_number};
  static argcheck_T arg2_list_number_bool[] = {arg_list_number, arg_bool};
! static argcheck_T arg2_listblobmod_item[] = {arg_list_or_blob_mod, 
arg_item_of_prev};
  static argcheck_T arg2_lnum[] = {arg_lnum, arg_lnum};
  static argcheck_T arg2_lnum_number[] = {arg_lnum, arg_number};
  static argcheck_T arg2_number[] = {arg_number, arg_number};
***************
*** 1036,1042 ****
  static argcheck_T arg13_cursor[] = {arg_cursor1, arg_number, arg_number};
  static argcheck_T arg12_deepcopy[] = {NULL, arg_bool};
  static argcheck_T arg12_execute[] = {arg_string_or_list_string, arg_string};
! static argcheck_T arg23_extend[] = {arg_list_or_dict, arg_same_as_prev, 
arg_extend3};
  static argcheck_T arg23_extendnew[] = {arg_list_or_dict, 
arg_same_struct_as_prev, arg_extend3};
  static argcheck_T arg23_get[] = {arg_get1, arg_string_or_nr, NULL};
  static argcheck_T arg14_glob[] = {arg_string, arg_bool, arg_bool, arg_bool};
--- 1080,1086 ----
  static argcheck_T arg13_cursor[] = {arg_cursor1, arg_number, arg_number};
  static argcheck_T arg12_deepcopy[] = {NULL, arg_bool};
  static argcheck_T arg12_execute[] = {arg_string_or_list_string, arg_string};
! static argcheck_T arg23_extend[] = {arg_list_or_dict_mod, arg_same_as_prev, 
arg_extend3};
  static argcheck_T arg23_extendnew[] = {arg_list_or_dict, 
arg_same_struct_as_prev, arg_extend3};
  static argcheck_T arg23_get[] = {arg_get1, arg_string_or_nr, NULL};
  static argcheck_T arg14_glob[] = {arg_string, arg_bool, arg_bool, arg_bool};
***************
*** 1554,1560 ****
                        ret_any,            f_abs},
      {"acos",          1, 1, FEARG_1,      arg1_float_or_nr,
                        ret_float,          f_acos},
!     {"add",           2, 2, FEARG_1,      arg2_listblob_item,
                        ret_first_arg,      f_add},
      {"and",           2, 2, FEARG_1,      arg2_number,
                        ret_number,         f_and},
--- 1598,1604 ----
                        ret_any,            f_abs},
      {"acos",          1, 1, FEARG_1,      arg1_float_or_nr,
                        ret_float,          f_acos},
!     {"add",           2, 2, FEARG_1,      arg2_listblobmod_item,
                        ret_first_arg,      f_add},
      {"and",           2, 2, FEARG_1,      arg2_number,
                        ret_number,         f_and},
*** ../vim-9.0.0622/src/proto/evalfunc.pro      2022-06-27 23:15:03.000000000 
+0100
--- src/proto/evalfunc.pro      2022-09-29 18:39:11.040861022 +0100
***************
*** 1,4 ****
--- 1,5 ----
  /* evalfunc.c */
+ int arg_type_modifiable(type_T *type, int arg_idx);
  char_u *get_function_name(expand_T *xp, int idx);
  char_u *get_expr_name(expand_T *xp, int idx);
  int find_internal_func(char_u *name);
*** ../vim-9.0.0622/src/vim9instr.c     2022-09-19 15:54:29.539117880 +0100
--- src/vim9instr.c     2022-09-29 18:40:49.408893607 +0100
***************
*** 1533,1538 ****
--- 1533,1540 ----
      // For checking the item type we use the declared type of the list and the
      // current type of the added item, adding a string to [1, 2] is OK.
      list_type = get_decl_type_on_stack(cctx, 1);
+     if (arg_type_modifiable(list_type, 1) == FAIL)
+       return FAIL;
      item_type = get_type_on_stack(cctx, 0);
      expected = list_type->tt_member;
      if (need_type(item_type, expected, -1, 0, cctx, FALSE, FALSE) == FAIL)
***************
*** 1554,1560 ****
  {
      type_T    *item_type;
  
!     // Caller already checked that blob_type is a blob.
      item_type = get_type_on_stack(cctx, 0);
      if (need_type(item_type, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
        return FAIL;
--- 1556,1564 ----
  {
      type_T    *item_type;
  
!     // Caller already checked that blob_type is a blob, check it is 
modifiable.
!     if (arg_type_modifiable(get_decl_type_on_stack(cctx, 1), 1) == FAIL)
!       return FAIL;
      item_type = get_type_on_stack(cctx, 0);
      if (need_type(item_type, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
        return FAIL;
*** ../vim-9.0.0622/src/testdir/test_vim9_builtin.vim   2022-09-23 
21:01:51.015203238 +0100
--- src/testdir/test_vim9_builtin.vim   2022-09-29 19:10:41.238892935 +0100
***************
*** 184,189 ****
--- 184,204 ----
    v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got 
number', 3)
  enddef
  
+ def Test_add_const()
+   var lines =<< trim END
+       const l = [1, 2]
+       add(l, 3)
+   END
+   v9.CheckDefFailure(lines, 'E1307: Argument 1: Trying to modify a const 
list<number>')
+ 
+   lines =<< trim END
+       const b = 0z0102
+       add(b,  0z03)
+   END
+   v9.CheckDefFailure(lines, 'E1307: Argument 1: Trying to modify a const 
blob')
+ enddef
+ 
+ 
  def Test_and()
    v9.CheckDefAndScriptFailure(['and("x", 0x2)'], ['E1013: Argument 1: type 
mismatch, expected number but got string', 'E1210: Number required for argument 
1'])
    v9.CheckDefAndScriptFailure(['and(0x1, "x")'], ['E1013: Argument 2: type 
mismatch, expected number but got string', 'E1210: Number required for argument 
2'])
***************
*** 1181,1186 ****
--- 1196,1224 ----
    v9.CheckScriptFailure(lines, 'E1001: Variable not found: m')
  enddef
  
+ def Test_extend_const()
+   var lines =<< trim END
+       const l = [1, 2]
+       extend(l, [3])
+   END
+   v9.CheckDefFailure(lines, 'E1307: Argument 1: Trying to modify a const 
list<number>')
+ 
+   lines =<< trim END
+       const d = {a: 1, b: 2}
+       extend(d, {c: 3})
+   END
+   v9.CheckDefFailure(lines, 'E1307: Argument 1: Trying to modify a const 
dict<number>')
+ 
+   # item in a for loop is const
+   lines =<< trim END
+       var l: list<dict<any>> = [{n: 1}]
+       for item in l
+         item->extend({x: 2})
+       endfor
+   END
+   v9.CheckDefFailure(lines, 'E1307: Argument 1: Trying to modify a const 
dict<any>')
+ enddef
+ 
  def Test_extendnew()
    assert_equal([1, 2, 'a'], extendnew([1, 2], ['a']))
    assert_equal({one: 1, two: 'a'}, extendnew({one: 1}, {two: 'a'}))
*** ../vim-9.0.0622/src/testdir/test_vim9_script.vim    2022-09-21 
18:59:10.671074961 +0100
--- src/testdir/test_vim9_script.vim    2022-09-29 19:06:04.571316796 +0100
***************
*** 2471,2480 ****
    lines =<< trim END
        var l: list<dict<any>> = [{n: 1}]
        for item: dict<number> in l
!         item->extend({s: ''})
        endfor
    END
!   v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, 
expected dict<number> but got dict<string>')
  
    lines =<< trim END
        for a in range(3)
--- 2471,2481 ----
    lines =<< trim END
        var l: list<dict<any>> = [{n: 1}]
        for item: dict<number> in l
!         var d = {s: ''}
!         d->extend(item)
        endfor
    END
!   v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, 
expected dict<string> but got dict<number>')
  
    lines =<< trim END
        for a in range(3)
*** ../vim-9.0.0622/src/version.c       2022-09-29 13:50:04.715222469 +0100
--- src/version.c       2022-09-29 15:41:56.169472977 +0100
***************
*** 701,702 ****
--- 701,704 ----
  {   /* Add new patch number below this line */
+ /**/
+     623,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
221. Your wife melts your keyboard in the oven.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            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/20220929181548.80FDC1C044A%40moolenaar.net.

Raspunde prin e-mail lui