patch 8.2.3996: Vim9: type checking lacks information about declared type
Problem: Vim9: type checking for list and dict lacks information about
declared type.
Solution: Add dv_decl_type and lv_decl_type. Refactor the type stack to
store two types in each entry.
diff --git a/src/vim9instr.c b/src/vim9instr.c
index 49936c3..4961695 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -57,34 +57,46 @@
isn_T *
generate_instr_drop(cctx_T *cctx, isntype_T isn_type, int drop)
{
- garray_T *stack = &cctx->ctx_type_stack;
-
RETURN_NULL_IF_SKIP(cctx);
- stack->ga_len -= drop;
+ cctx->ctx_type_stack.ga_len -= drop;
return generate_instr(cctx, isn_type);
}
/*
- * Generate instruction "isn_type" and put "type" on the type stack.
+ * Generate instruction "isn_type" and put "type" on the type stack,
+ * use "decl_type" for the declared type.
*/
isn_T *
-generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type)
+generate_instr_type2(
+ cctx_T *cctx,
+ isntype_T isn_type,
+ type_T *type,
+ type_T *decl_type)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
if ((isn = generate_instr(cctx, isn_type)) == NULL)
return NULL;
- if (GA_GROW_FAILS(stack, 1))
+ if (push_type_stack2(cctx, type == NULL ? &t_any : type,
+ decl_type == NULL ? &t_any : decl_type) == FAIL)
return NULL;
- ((type_T **)stack->ga_data)[stack->ga_len] = type == NULL ? &t_any : type;
- ++stack->ga_len;
return isn;
}
/*
+ * Generate instruction "isn_type" and put "type" on the type stack.
+ * Uses "any" for the declared type, which works for constants. For declared
+ * variables use generate_instr_type2().
+ */
+ isn_T *
+generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type)
+{
+ return generate_instr_type2(cctx, isn_type, type, &t_any);
+}
+
+/*
* Generate an ISN_DEBUG instruction.
*/
isn_T *
@@ -111,12 +123,11 @@
{
isn_T *isn;
isntype_T isntype = ISN_2STRING;
- garray_T *stack = &cctx->ctx_type_stack;
- type_T **type;
+ type_T *type;
RETURN_OK_IF_SKIP(cctx);
- type = ((type_T **)stack->ga_data) + stack->ga_len + offset;
- switch ((*type)->tt_type)
+ type = get_type_on_stack(cctx, -1 - offset);
+ switch (type->tt_type)
{
// nothing to be done
case VAR_STRING: return OK;
@@ -152,11 +163,11 @@
case VAR_JOB:
case VAR_CHANNEL:
case VAR_INSTR:
- to_string_error((*type)->tt_type);
+ to_string_error(type->tt_type);
return FAIL;
}
- *type = &t_string;
+ set_type_on_stack(cctx, &t_string, -1 - offset);
if ((isn = generate_instr(cctx, isntype)) == NULL)
return FAIL;
isn->isn_arg.tostring.offset = offset;
@@ -193,7 +204,6 @@
type_T *type2,
exprtype_T expr_type)
{
- garray_T *stack = &cctx->ctx_type_stack;
isn_T *isn = generate_instr_drop(cctx,
vartype == VAR_NUMBER ? ISN_OPNR
: vartype == VAR_LIST ? ISN_ADDLIST
@@ -225,7 +235,7 @@
if (vartype == VAR_LIST
&& type1->tt_type == VAR_LIST && type2->tt_type == VAR_LIST
&& type1->tt_member != type2->tt_member)
- (((type_T **)stack->ga_data)[stack->ga_len - 1]) = &t_list_any;
+ set_type_on_stack(cctx, &t_list_any, 0);
return isn == NULL ? FAIL : OK;
}
@@ -256,7 +266,6 @@
int
generate_two_op(cctx_T *cctx, char_u *op)
{
- garray_T *stack = &cctx->ctx_type_stack;
type_T *type1;
type_T *type2;
vartype_T vartype;
@@ -265,8 +274,8 @@
RETURN_OK_IF_SKIP(cctx);
// Get the known type of the two items on the stack.
- type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2];
- type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+ type1 = get_type_on_stack(cctx, 1);
+ type2 = get_type_on_stack(cctx, 0);
vartype = operator_type(type1, type2);
switch (*op)
@@ -323,7 +332,7 @@
&& (type2->tt_type == VAR_NUMBER || type2->tt_type == VAR_FLOAT))
type = &t_float;
#endif
- ((type_T **)stack->ga_data)[stack->ga_len - 1] = type;
+ set_type_on_stack(cctx, type, 0);
}
return OK;
@@ -415,8 +424,8 @@
// Get the known type of the two items on the stack. If they are matching
// use a type-specific instruction. Otherwise fall back to runtime type
// checking.
- type1 = ((type_T **)stack->ga_data)[stack->ga_len - 2]->tt_type;
- type2 = ((type_T **)stack->ga_data)[stack->ga_len - 1]->tt_type;
+ type1 = get_type_on_stack(cctx, 1)->tt_type;
+ type2 = get_type_on_stack(cctx, 0)->tt_type;
isntype = get_compare_isn(exprtype, type1, type2);
if (isntype == ISN_DROP)
return FAIL;
@@ -430,7 +439,7 @@
if (stack->ga_len >= 2)
{
--stack->ga_len;
- ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool;
+ set_type_on_stack(cctx, &t_bool, 0);
}
return OK;
@@ -444,7 +453,6 @@
generate_2BOOL(cctx_T *cctx, int invert, int offset)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL)
@@ -453,7 +461,7 @@
isn->isn_arg.tobool.offset = offset;
// type becomes bool
- ((type_T **)stack->ga_data)[stack->ga_len + offset] = &t_bool;
+ set_type_on_stack(cctx, &t_bool, -1 - offset);
return OK;
}
@@ -465,14 +473,13 @@
generate_COND2BOOL(cctx_T *cctx)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_COND2BOOL)) == NULL)
return FAIL;
// type becomes bool
- ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool;
+ set_type_on_stack(cctx, &t_bool, 0);
return OK;
}
@@ -485,7 +492,6 @@
int argidx)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_CHECKTYPE)) == NULL)
@@ -495,7 +501,7 @@
isn->isn_arg.type.ct_arg_idx = (int8_T)argidx;
// type becomes expected
- ((type_T **)stack->ga_data)[stack->ga_len + offset] = expected;
+ set_type_on_stack(cctx, expected, -1 - offset);
return OK;
}
@@ -567,7 +573,6 @@
generate_PUSHNR(cctx_T *cctx, varnumber_T number)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_type(cctx, ISN_PUSHNR, &t_number)) == NULL)
@@ -576,7 +581,7 @@
if (number == 0 || number == 1)
// A 0 or 1 number can also be used as a bool.
- ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_number_bool;
+ set_type_on_stack(cctx, &t_number_bool, 0);
return OK;
}
@@ -747,9 +752,7 @@
generate_GETITEM(cctx_T *cctx, int index, int with_op)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
- type_T *type = ((type_T **)stack->ga_data)[stack->ga_len
- - (with_op ? 2 : 1)];
+ type_T *type = get_type_on_stack(cctx, with_op ? 1 : 0);
type_T *item_type = &t_any;
RETURN_OK_IF_SKIP(cctx);
@@ -767,11 +770,7 @@
isn->isn_arg.getitem.gi_with_op = with_op;
// add the item type to the type stack
- if (GA_GROW_FAILS(stack, 1))
- return FAIL;
- ((type_T **)stack->ga_data)[stack->ga_len] = item_type;
- ++stack->ga_len;
- return OK;
+ return push_type_stack(cctx, item_type);
}
/*
@@ -895,7 +894,7 @@
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
- if ((isn = generate_instr_type(cctx, isn_type, type)) == NULL)
+ if ((isn = generate_instr_type2(cctx, isn_type, type, type)) == NULL)
return FAIL;
if (name != NULL)
isn->isn_arg.string = vim_strsave(name);
@@ -918,7 +917,7 @@
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
- if ((isn = generate_instr_type(cctx, ISN_LOADOUTER, type)) == NULL)
+ if ((isn = generate_instr_type2(cctx, ISN_LOADOUTER, type, type)) == NULL)
return FAIL;
isn->isn_arg.outer.outer_idx = idx;
isn->isn_arg.outer.outer_depth = nesting;
@@ -1050,34 +1049,27 @@
generate_NEWLIST(cctx_T *cctx, int count)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
+ type_T *member_type;
+ type_T *decl_member_type;
type_T *type;
- type_T *member;
+ type_T *decl_type;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_NEWLIST)) == NULL)
return FAIL;
isn->isn_arg.number = count;
- // get the member type from all the items on the stack.
- if (count == 0)
- member = &t_unknown;
- else
- member = get_member_type_from_stack(
- ((type_T **)stack->ga_data) + stack->ga_len, count, 1,
- cctx->ctx_type_list);
- type = get_list_type(member, cctx->ctx_type_list);
+ // Get the member type and the declared member type from all the items on
+ // the stack.
+ member_type = get_member_type_from_stack(count, 1, &decl_member_type, cctx);
+ type = get_list_type(member_type, cctx->ctx_type_list);
+ decl_type = get_list_type(decl_member_type, cctx->ctx_type_list);
// drop the value types
- stack->ga_len -= count;
+ cctx->ctx_type_stack.ga_len -= count;
// add the list type to the type stack
- if (GA_GROW_FAILS(stack, 1))
- return FAIL;
- ((type_T **)stack->ga_data)[stack->ga_len] = type;
- ++stack->ga_len;
-
- return OK;
+ return push_type_stack2(cctx, type, decl_type);
}
/*
@@ -1087,33 +1079,26 @@
generate_NEWDICT(cctx_T *cctx, int count)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
+ type_T *member_type;
+ type_T *decl_member_type;
type_T *type;
- type_T *member;
+ type_T *decl_type;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_NEWDICT)) == NULL)
return FAIL;
isn->isn_arg.number = count;
- if (count == 0)
- member = &t_void;
- else
- member = get_member_type_from_stack(
- ((type_T **)stack->ga_data) + stack->ga_len, count, 2,
- cctx->ctx_type_list);
- type = get_dict_type(member, cctx->ctx_type_list);
+ member_type = get_member_type_from_stack(count, 2,
+ &decl_member_type, cctx);
+ type = get_dict_type(member_type, cctx->ctx_type_list);
+ decl_type = get_dict_type(decl_member_type, cctx->ctx_type_list);
// drop the key and value types
- stack->ga_len -= 2 * count;
+ cctx->ctx_type_stack.ga_len -= 2 * count;
// add the dict type to the type stack
- if (GA_GROW_FAILS(stack, 1))
- return FAIL;
- ((type_T **)stack->ga_data)[stack->ga_len] = type;
- ++stack->ga_len;
-
- return OK;
+ return push_type_stack2(cctx, type, decl_type);
}
/*
@@ -1123,7 +1108,7 @@
generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
+ type_T *type;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
@@ -1139,13 +1124,8 @@
if (ufunc->uf_flags & FC_CLOSURE)
cctx->ctx_ufunc->uf_flags |= FC_CLOSURE;
- if (GA_GROW_FAILS(stack, 1))
- return FAIL;
- ((type_T **)stack->ga_data)[stack->ga_len] =
- ufunc->uf_func_type == NULL ? &t_func_any : ufunc->uf_func_type;
- ++stack->ga_len;
-
- return OK;
+ type = ufunc->uf_func_type == NULL ? &t_func_any : ufunc->uf_func_type;
+ return push_type_stack(cctx, type);
}
/*
@@ -1237,20 +1217,14 @@
generate_FOR(cctx_T *cctx, int loop_idx)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_FOR)) == NULL)
return FAIL;
isn->isn_arg.forloop.for_idx = loop_idx;
- if (GA_GROW_FAILS(stack, 1))
- return FAIL;
// type doesn't matter, will be stored next
- ((type_T **)stack->ga_data)[stack->ga_len] = &t_any;
- ++stack->ga_len;
-
- return OK;
+ return push_type_stack(cctx, &t_any);
}
/*
* Generate an ISN_TRYCONT instruction.
@@ -1281,9 +1255,11 @@
isn_T *isn;
garray_T *stack = &cctx->ctx_type_stack;
int argoff;
- type_T **argtypes = NULL;
- type_T *shuffled_argtypes[MAX_FUNC_ARGS];
- type_T *maptype = NULL;
+ type2_T *typep;
+ type2_T *argtypes = NULL;
+ type2_T shuffled_argtypes[MAX_FUNC_ARGS];
+ type2_T *maptype = NULL;
+ type_T *type;
RETURN_OK_IF_SKIP(cctx);
argoff = check_internal_func(func_idx, argcount);
@@ -1301,22 +1277,30 @@
if (argcount > 0)
{
// Check the types of the arguments.
- argtypes = ((type_T **)stack->ga_data) + stack->ga_len - argcount;
+ typep = ((type2_T *)stack->ga_data) + stack->ga_len - argcount;
if (method_call && argoff > 1)
{
int i;
for (i = 0; i < argcount; ++i)
shuffled_argtypes[i] = (i < argoff - 1)
- ? argtypes[i + 1]
- : (i == argoff - 1) ? argtypes[0] : argtypes[i];
+ ? typep[i + 1]
+ : (i == argoff - 1) ? typep[0] : typep[i];
+ argtypes = shuffled_argtypes;
+ }
+ else
+ {
+ int i;
+
+ for (i = 0; i < argcount; ++i)
+ shuffled_argtypes[i] = typep[i];
argtypes = shuffled_argtypes;
}
if (internal_func_check_arg_types(argtypes, func_idx, argcount,
cctx) == FAIL)
return FAIL;
if (internal_func_is_map(func_idx))
- maptype = *argtypes;
+ maptype = argtypes;
}
if ((isn = generate_instr(cctx, ISN_BCALL)) == NULL)
@@ -1326,16 +1310,14 @@
// Drop the argument types and push the return type.
stack->ga_len -= argcount;
- if (GA_GROW_FAILS(stack, 1))
+ type = internal_func_ret_type(func_idx, argcount, argtypes);
+ if (push_type_stack(cctx, type) == FAIL)
return FAIL;
- ((type_T **)stack->ga_data)[stack->ga_len] =
- internal_func_ret_type(func_idx, argcount, argtypes);
- ++stack->ga_len;
- if (maptype != NULL && maptype->tt_member != NULL
- && maptype->tt_member != &t_any)
+ if (maptype != NULL && maptype[0].type_curr->tt_member != NULL
+ && maptype[0].type_curr->tt_member != &t_any)
// Check that map() didn't change the item types.
- generate_TYPECHECK(cctx, maptype, -1, 1);
+ generate_TYPECHECK(cctx, maptype[0].type_curr, -1, 1);
return OK;
}
@@ -1347,14 +1329,13 @@
int
generate_LISTAPPEND(cctx_T *cctx)
{
- garray_T *stack = &cctx->ctx_type_stack;
type_T *list_type;
type_T *item_type;
type_T *expected;
// Caller already checked that list_type is a list.
- list_type = ((type_T **)stack->ga_data)[stack->ga_len - 2];
- item_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+ list_type = get_type_on_stack(cctx, 1);
+ 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)
return FAIL;
@@ -1362,7 +1343,7 @@
if (generate_instr(cctx, ISN_LISTAPPEND) == NULL)
return FAIL;
- --stack->ga_len; // drop the argument
+ --cctx->ctx_type_stack.ga_len; // drop the argument
return OK;
}
@@ -1373,18 +1354,17 @@
int
generate_BLOBAPPEND(cctx_T *cctx)
{
- garray_T *stack = &cctx->ctx_type_stack;
type_T *item_type;
// Caller already checked that blob_type is a blob.
- item_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+ item_type = get_type_on_stack(cctx, 0);
if (need_type(item_type, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
return FAIL;
if (generate_instr(cctx, ISN_BLOBAPPEND) == NULL)
return FAIL;
- --stack->ga_len; // drop the argument
+ --cctx->ctx_type_stack.ga_len; // drop the argument
return OK;
}
@@ -1396,7 +1376,6 @@
generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
int regular_args = ufunc->uf_args.ga_len;
int argcount = pushed_argcount;
@@ -1424,7 +1403,7 @@
type_T *expected;
type_T *actual;
- actual = ((type_T **)stack->ga_data)[stack->ga_len - argcount + i];
+ actual = get_type_on_stack(cctx, argcount - i - 1);
if (actual == &t_special
&& i >= regular_args - ufunc->uf_def_args.ga_len)
{
@@ -1479,14 +1458,11 @@
isn->isn_arg.ufunc.cuf_argcount = argcount;
}
- stack->ga_len -= argcount; // drop the arguments
- if (GA_GROW_FAILS(stack, 1))
- return FAIL;
- // add return value
- ((type_T **)stack->ga_data)[stack->ga_len] = ufunc->uf_ret_type;
- ++stack->ga_len;
+ // drop the argument types
+ cctx->ctx_type_stack.ga_len -= argcount;
- return OK;
+ // add return type
+ return push_type_stack(cctx, ufunc->uf_ret_type);
}
/*
@@ -1496,7 +1472,6 @@
generate_UCALL(cctx_T *cctx, char_u *name, int argcount)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_UCALL)) == NULL)
@@ -1504,14 +1479,11 @@
isn->isn_arg.ufunc.cuf_name = vim_strsave(name);
isn->isn_arg.ufunc.cuf_argcount = argcount;
- stack->ga_len -= argcount; // drop the arguments
- if (GA_GROW_FAILS(stack, 1))
- return FAIL;
- // add return value
- ((type_T **)stack->ga_data)[stack->ga_len] = &t_any;
- ++stack->ga_len;
+ // drop the argument types
+ cctx->ctx_type_stack.ga_len -= argcount;
- return OK;
+ // add return value
+ return push_type_stack(cctx, &t_any);
}
/*
@@ -1527,7 +1499,6 @@
int at_top)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
type_T *ret_type;
RETURN_OK_IF_SKIP(cctx);
@@ -1557,8 +1528,7 @@
for (i = 0; i < argcount; ++i)
{
int offset = -argcount + i - (at_top ? 0 : 1);
- type_T *actual = ((type_T **)stack->ga_data)[
- stack->ga_len + offset];
+ type_T *actual = get_type_on_stack(cctx, -1 - offset);
type_T *expected;
if (varargs && i >= type->tt_argcount - 1)
@@ -1594,10 +1564,11 @@
isn->isn_arg.pfunc.cpf_top = at_top;
isn->isn_arg.pfunc.cpf_argcount = argcount;
- stack->ga_len -= argcount; // drop the arguments
+ // drop the arguments and the funcref/partial
+ cctx->ctx_type_stack.ga_len -= argcount + 1;
- // drop the funcref/partial, get back the return value
- ((type_T **)stack->ga_data)[stack->ga_len - 1] = ret_type;
+ // push the return value
+ push_type_stack(cctx, ret_type);
// If partial is above the arguments it must be cleared and replaced with
// the return value.
@@ -1614,7 +1585,6 @@
generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
type_T *type;
RETURN_OK_IF_SKIP(cctx);
@@ -1623,7 +1593,7 @@
isn->isn_arg.string = vim_strnsave(name, len);
// check for dict type
- type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+ type = get_type_on_stack(cctx, 0);
if (type->tt_type != VAR_DICT && type != &t_any && type != &t_unknown)
{
char *tofree;
@@ -1636,8 +1606,9 @@
// change dict type to dict member type
if (type->tt_type == VAR_DICT)
{
- ((type_T **)stack->ga_data)[stack->ga_len - 1] =
- type->tt_member == &t_unknown ? &t_any : type->tt_member;
+ type_T *ntype = type->tt_member == &t_unknown
+ ? &t_any : type->tt_member;
+ set_type_on_stack(cctx, ntype, 0);
}
return OK;
@@ -1734,19 +1705,13 @@
generate_LEGACY_EVAL(cctx_T *cctx, char_u *line)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_LEGACY_EVAL)) == NULL)
return FAIL;
isn->isn_arg.string = vim_strsave(line);
- if (GA_GROW_FAILS(stack, 1))
- return FAIL;
- ((type_T **)stack->ga_data)[stack->ga_len] = &t_any;
- ++stack->ga_len;
-
- return OK;
+ return push_type_stack(cctx, &t_any);
}
int
@@ -1767,17 +1732,12 @@
generate_RANGE(cctx_T *cctx, char_u *range)
{
isn_T *isn;
- garray_T *stack = &cctx->ctx_type_stack;
if ((isn = generate_instr(cctx, ISN_RANGE)) == NULL)
return FAIL;
isn->isn_arg.string = range;
- if (GA_GROW_FAILS(stack, 1))
- return FAIL;
- ((type_T **)stack->ga_data)[stack->ga_len] = &t_number;
- ++stack->ga_len;
- return OK;
+ return push_type_stack(cctx, &t_number);
}
int