]> granicus.if.org Git - vim/commitdiff
patch 9.0.0623: error for modifying a const is not detected at compile time v9.0.0623
authorBram Moolenaar <Bram@vim.org>
Thu, 29 Sep 2022 18:14:42 +0000 (19:14 +0100)
committerBram Moolenaar <Bram@vim.org>
Thu, 29 Sep 2022 18:14:42 +0000 (19:14 +0100)
Problem:    Error for modifying a const is not detected at compile time.
Solution:   Add TTFLAG_CONST and check for it in add() and extend().

12 files changed:
src/errors.h
src/evalfunc.c
src/globals.h
src/proto/evalfunc.pro
src/proto/vim9type.pro
src/structs.h
src/testdir/test_vim9_builtin.vim
src/testdir/test_vim9_script.vim
src/version.c
src/vim9compile.c
src/vim9instr.c
src/vim9type.c

index 7f23cc8553b8e517a6cb6a71eee710368c3a58fc..868ed572143fa0f8f173c7859a2a67373583be56 100644 (file)
@@ -3332,4 +3332,6 @@ EXTERN char e_cannot_use_length_endcol_and_endlnum_with_text[]
 #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
index 857f211a21dba63d236377e131e1bc166519393f..211e12ce4bd1b14cb662929c12a645d52270e323 100644 (file)
@@ -221,6 +221,22 @@ check_arg_type(
            context->arg_cctx, FALSE, FALSE);
 }
 
+/*
+ * 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.
  */
@@ -323,6 +339,20 @@ arg_list_or_blob(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
     return FAIL;
 }
 
+/*
+ * 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
  */
@@ -467,6 +497,20 @@ arg_list_or_dict(type_T *type, type_T *decl_type UNUSED, argcontext_T *context)
     return FAIL;
 }
 
+/*
+ * 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.
  */
@@ -979,7 +1023,7 @@ static argcheck_T arg2_list_any_number[] = {arg_list_any, arg_number};
 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_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,7 +1080,7 @@ static argcheck_T arg24_count[] = {arg_string_or_list_or_dict, NULL, arg_bool, a
 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_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,7 +1598,7 @@ static funcentry_T global_functions[] =
                        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,
+    {"add",            2, 2, FEARG_1,      arg2_listblobmod_item,
                        ret_first_arg,      f_add},
     {"and",            2, 2, FEARG_1,      arg2_number,
                        ret_number,         f_and},
index fee2951e0a559c8ab4c6f59ac0d6c12427e649fb..aa0efb89a851765278f0253ffaf63ec9687cd0f4 100644 (file)
@@ -396,65 +396,301 @@ EXTERN int       want_garbage_collect INIT(= FALSE);
 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);
+// 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
+;
 
-#ifdef FEAT_EVAL
 EXTERN int     did_source_packages INIT(= FALSE);
-#endif
+#endif // FEAT_EVAL
 
 // Magic number used for hashitem "hi_key" value indicating a deleted item.
 // Only the address is used.
index 5103588d7092a34b7c6ba007ffdb031790694ae7..da1c32411954983dbf36e788ba820552a746f6f2 100644 (file)
@@ -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);
index aaa1a71eb2ed565cd27fd3fc145135d5f7f14d59..68bb8fa8c2d45e5792c9296e9762a28c7761a335 100644 (file)
@@ -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);
index 27fb5bdf8447f566ef3bbd76248473fe80519bcb..4caff2db726f2096fdda6fe547e391a416fb9803 100644 (file)
@@ -1445,10 +1445,10 @@ typedef struct {
     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
+#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.
index e09f913709e02c8323885304bcecc4172ecfc591..4ebf65a1de7995fe44814810d29251d11d221d4a 100644 (file)
@@ -184,6 +184,21 @@ def Test_add_list()
   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,6 +1196,29 @@ def Test_extend_with_error_function()
   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'}))
index 54b4ea16d06282199d6d39c44ae194317191800d..fd07fbfaee8ba4b76a7aada6aa21e1fa813dfb9b 100644 (file)
@@ -2471,10 +2471,11 @@ def Test_for_loop_fails()
   lines =<< trim END
       var l: list<dict<any>> = [{n: 1}]
       for item: dict<number> in l
-        item->extend({s: ''})
+        var d = {s: ''}
+        d->extend(item)
       endfor
   END
-  v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected dict<number> but got dict<string>')
+  v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected dict<string> but got dict<number>')
 
   lines =<< trim END
       for a in range(3)
index 95042e8c1f7973f69e920df50bbeda9d0e8ed1d8..bcc0b24be5aadd8b8939b5087b636c33f294e103 100644 (file)
@@ -699,6 +699,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    623,
 /**/
     622,
 /**/
index 74ba88fda87fc717bf0bca12c13819473b776ced..522534597634a9090c20e99eaadaa35a926e3be7 100644 (file)
@@ -462,6 +462,29 @@ need_type(
                                                cctx, silent, actual_is_const);
 }
 
+/*
+ * 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,7 +520,12 @@ reserve_local(
 
     lvar->lv_name = vim_strnsave(name, len == 0 ? STRLEN(name) : len);
     lvar->lv_const = isConst;
-    lvar->lv_type = type;
+    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,19 +2332,22 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                        }
                        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)
-                               lhs.lhs_lvar->lv_type = &t_list_any;
+                               type = &t_list_any;
                            else if (rhs_type == &t_dict_empty)
-                               lhs.lhs_lvar->lv_type = &t_dict_any;
+                               type = &t_dict_any;
                            else if (rhs_type == &t_unknown)
-                               lhs.lhs_lvar->lv_type = &t_any;
+                               type = &t_any;
                            else
                            {
-                               lhs.lhs_lvar->lv_type = rhs_type;
+                               type = rhs_type;
                                inferred_type = rhs_type;
                            }
+                           set_var_type(lhs.lhs_lvar, type, cctx);
                        }
                    }
                    else if (*op == '=')
index 85a49a893446f9624748c4986b64841509196c63..18dad116889d6f5ef4c3387a59667806764c31bf 100644 (file)
@@ -1533,6 +1533,8 @@ generate_LISTAPPEND(cctx_T *cctx)
     // 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,7 +1556,9 @@ generate_BLOBAPPEND(cctx_T *cctx)
 {
     type_T     *item_type;
 
-    // Caller already checked that blob_type is a blob.
+    // 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;
index 843e5abd83b97638ef139eb1cf74c9bb79a83cf5..6f205546d6e6b9f4d585f9801537b903c6448aad 100644 (file)
@@ -45,6 +45,30 @@ get_type_ptr(garray_T *type_gap)
     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)
 {