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 <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/eval.c b/src/eval.c
index 6ce5918..f4f8c05 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1489,8 +1489,17 @@
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 @@
// 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 @@
{
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 dcb8978..d6d4a0f 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -4692,6 +4692,7 @@
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 1848c01..616e224 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -3707,4 +3707,46 @@
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<list<any>> = [['a']]
+ var v = [[10]]
+ l[0]->add(v)
+ l[0][1][0] = [{x: 20}]
+ END
+ v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected list<number> but got list<dict<number>>', 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 f34048e..dc7a029 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 952,
+/**/
951,
/**/
950,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 7ab6813..f20ff40 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2216,6 +2216,25 @@
}
/*
+ * 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 @@
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 @@
}
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 @@
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 @@
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));
}