To: vim_dev@googlegroups.com Subject: Patch 9.0.0623 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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') + + 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') + + 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') + + # item in a for loop is const + lines =<< trim END + var l: list> = [{n: 1}] + for item in l + item->extend({x: 2}) + endfor + END + v9.CheckDefFailure(lines, 'E1307: Argument 1: Trying to modify a const dict') + 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> = [{n: 1}] for item: dict in l ! item->extend({s: ''}) endfor END ! v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected dict but got dict') lines =<< trim END for a in range(3) --- 2471,2481 ---- lines =<< trim END var l: list> = [{n: 1}] for item: dict in l ! var d = {s: ''} ! d->extend(item) endfor END ! v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected dict but got dict') 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 -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///