From: Yegappan Lakshmanan Date: Sun, 22 Dec 2024 13:44:35 +0000 (+0100) Subject: patch 9.1.0952: Vim9: missing type checking for any type assignment X-Git-Tag: v9.1.0952^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=92195ae72f141a1d454d930a0b395c63d08f5fe0;p=thirdparty%2Fvim.git patch 9.1.0952: Vim9: missing type checking for any type assignment Problem: Vim9: missing type checking for any type assignment (Ernie Rael) Solution: when assigning to a list item, if the type of the LHS item is any, then use the list item type (Yegappan Lakshmanan) fixes: #15208 closes: #16274 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- diff --git a/src/eval.c b/src/eval.c index 6ce5918851..f4f8c058db 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1489,8 +1489,17 @@ get_lval_list( return FAIL; if (lp->ll_valtype != NULL && !lp->ll_range) + { // use the type of the member - lp->ll_valtype = lp->ll_valtype->tt_member; + if (lp->ll_valtype->tt_member != NULL) + lp->ll_valtype = lp->ll_valtype->tt_member; + else + // If the LHS member type is not known (VAR_ANY), then get it from + // the list item (after indexing) + lp->ll_valtype = typval2type(&lp->ll_li->li_tv, get_copyID(), + &lp->ll_type_list, TVTT_DO_MEMBER); + + } /* * May need to find the item or absolute index for the second @@ -1994,6 +2003,7 @@ get_lval( // Clear everything in "lp". CLEAR_POINTER(lp); + ga_init2(&lp->ll_type_list, sizeof(type_T *), 10); if (skip || (flags & GLV_COMPILING)) { @@ -2178,6 +2188,7 @@ clear_lval(lval_T *lp) { vim_free(lp->ll_exp_name); vim_free(lp->ll_newkey); + clear_type_list(&lp->ll_type_list); } /* diff --git a/src/structs.h b/src/structs.h index dcb8978fed..d6d4a0fef7 100644 --- a/src/structs.h +++ b/src/structs.h @@ -4692,6 +4692,7 @@ typedef struct lval_S int ll_oi; // The object/class member index int ll_is_root; // TRUE if ll_tv is the lval_root, like a // plain object/class. ll_tv is variable. + garray_T ll_type_list; // list of pointers to allocated types } lval_T; /** diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 1848c016c1..616e224915 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -3707,4 +3707,46 @@ def Test_override_script_local_func() v9.CheckScriptFailure(lines, 'E705: Variable name conflicts with existing function: MyFunc', 5) enddef +" Test for doing a type check at runtime for a list member type +def Test_nested_type_check() + var lines =<< trim END + var d = {a: [10], b: [20]} + var l = d->items() + l[0][1][0] = 'abc' + END + v9.CheckSourceDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got string') + + lines =<< trim END + vim9script + var d = {a: [10], b: [20]} + var l = d->items() + l[0][1][0] = 30 + assert_equal([['a', [30]], ['b', [20]]], l) + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + def Foo() + var d = {a: [10], b: [20]} + var l = d->items() + l[0][1][0] = 30 + assert_equal([['a', [30]], ['b', [20]]], l) + enddef + Foo() + END + v9.CheckScriptSuccess(lines) + + # Test for modifying a List item added using add() with a different type. + lines =<< trim END + vim9script + + var l: list> = [['a']] + var v = [[10]] + l[0]->add(v) + l[0][1][0] = [{x: 20}] + END + v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected list but got list>', 6) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index f34048e91c..dc7a029f4b 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 952, /**/ 951, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 7ab6813a69..f20ff40647 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2215,6 +2215,25 @@ handle_debug(isn_T *iptr, ectx_T *ectx) vim_free(line); } +/* + * Do a runtime check of the RHS value against the LHS List member type. + * This is used by the STOREINDEX instruction to perform a type check + * at runtime if compile time type check cannot be performed (VAR_ANY). + * Returns FAIL if there is a type mismatch. + */ + static int +storeindex_check_list_member_type( + list_T *lhs_list, + typval_T *rhs_tv, + ectx_T *ectx) +{ + if (lhs_list->lv_type == NULL || lhs_list->lv_type->tt_member == NULL) + return OK; + + return check_typval_type(lhs_list->lv_type->tt_member, rhs_tv, + ectx->ec_where); +} + /* * Store a value in a list, dict, blob or object variable. * Returns OK, FAIL or NOTDONE (uncatchable error). @@ -2228,6 +2247,7 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx) long lidx = 0; typval_T *tv_dest = STACK_TV_BOT(-1); int status = OK; + int check_rhs_type = FALSE; if (tv_idx->v_type == VAR_NUMBER) lidx = (long)tv_idx->vval.v_number; @@ -2247,6 +2267,7 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx) } else if (dest_type == VAR_ANY) { + check_rhs_type = TRUE; dest_type = tv_dest->v_type; if (dest_type == VAR_DICT) status = do_2string(tv_idx, TRUE, FALSE); @@ -2327,6 +2348,12 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx) semsg(_(e_list_index_out_of_range_nr), lidx); return FAIL; } + + // Do a runtime type check for VAR_ANY + if (check_rhs_type && + storeindex_check_list_member_type(list, tv, ectx) == FAIL) + return FAIL; + if (lidx < list->lv_len) { listitem_T *li = list_find(list, lidx); @@ -6304,7 +6331,7 @@ call_def_function( else if (check_typval_arg_type(expected, tv, NULL, argv_idx + 1) == FAIL) goto failed_early; - } + } if (!done) copy_tv(tv, STACK_TV_BOT(0)); }