Patch 8.2.2719
Problem: Vim9: appending to dict item doesn't work in a :def function.
Solution: Implement assignment with operator on indexed item.
Files: src/vim9compile.c, src/testdir/test_vim9_assign.vim
*** ../vim-8.2.2718/src/vim9compile.c 2021-04-04 20:49:46.626430253 +0200
--- src/vim9compile.c 2021-04-05 16:50:46.497420275 +0200
***************
*** 2650,2655 ****
--- 2650,2761 ----
}
/*
+ * Compile getting a member from a list/dict/string/blob. Stack has the
+ * indexable value and the index.
+ */
+ static int
+ compile_member(int is_slice, cctx_T *cctx)
+ {
+ type_T **typep;
+ garray_T *stack = &cctx->ctx_type_stack;
+ vartype_T vtype;
+ type_T *valtype;
+
+ // We can index a list and a dict. If we don't know the type
+ // we can use the index value type.
+ // TODO: If we don't know use an instruction to figure it out at
+ // runtime.
+ typep = ((type_T **)stack->ga_data) + stack->ga_len
+ - (is_slice ? 3 : 2);
+ vtype = (*typep)->tt_type;
+ valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+ // If the index is a string, the variable must be a Dict.
+ if (*typep == &t_any && valtype == &t_string)
+ vtype = VAR_DICT;
+ if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
+ {
+ if (need_type(valtype, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
+ return FAIL;
+ if (is_slice)
+ {
+ valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
+ if (need_type(valtype, &t_number, -2, 0, cctx,
+ FALSE, FALSE) == FAIL)
+ return FAIL;
+ }
+ }
+
+ if (vtype == VAR_DICT)
+ {
+ if (is_slice)
+ {
+ emsg(_(e_cannot_slice_dictionary));
+ return FAIL;
+ }
+ if ((*typep)->tt_type == VAR_DICT)
+ {
+ *typep = (*typep)->tt_member;
+ if (*typep == &t_unknown)
+ // empty dict was used
+ *typep = &t_any;
+ }
+ else
+ {
+ if (need_type(*typep, &t_dict_any, -2, 0, cctx,
+ FALSE, FALSE) == FAIL)
+ return FAIL;
+ *typep = &t_any;
+ }
+ if (may_generate_2STRING(-1, cctx) == FAIL)
+ return FAIL;
+ if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
+ return FAIL;
+ }
+ else if (vtype == VAR_STRING)
+ {
+ *typep = &t_string;
+ if ((is_slice
+ ? generate_instr_drop(cctx, ISN_STRSLICE, 2)
+ : generate_instr_drop(cctx, ISN_STRINDEX, 1)) == FAIL)
+ return FAIL;
+ }
+ else if (vtype == VAR_BLOB)
+ {
+ emsg("Sorry, blob index and slice not implemented yet");
+ return FAIL;
+ }
+ else if (vtype == VAR_LIST || *typep == &t_any)
+ {
+ if (is_slice)
+ {
+ if (generate_instr_drop(cctx,
+ vtype == VAR_LIST ? ISN_LISTSLICE : ISN_ANYSLICE,
+ 2) == FAIL)
+ return FAIL;
+ }
+ else
+ {
+ if ((*typep)->tt_type == VAR_LIST)
+ {
+ *typep = (*typep)->tt_member;
+ if (*typep == &t_unknown)
+ // empty list was used
+ *typep = &t_any;
+ }
+ if (generate_instr_drop(cctx,
+ vtype == VAR_LIST ? ISN_LISTINDEX : ISN_ANYINDEX, 1) == FAIL)
+ return FAIL;
+ }
+ }
+ else
+ {
+ emsg(_(e_string_list_dict_or_blob_required));
+ return FAIL;
+ }
+ return OK;
+ }
+
+ /*
* Generate an instruction to load script-local variable "name", without the
* leading "s:".
* Also finds imported variables.
***************
*** 3934,3943 ****
}
else if (**arg == '[')
{
- garray_T *stack = &cctx->ctx_type_stack;
- type_T **typep;
- type_T *valtype;
- vartype_T vtype;
int is_slice = FALSE;
// list index: list[123]
--- 4040,4045 ----
***************
*** 4004,4102 ****
}
*arg = *arg + 1;
! // We can index a list and a dict. If we don't know the type
! // we can use the index value type.
! // TODO: If we don't know use an instruction to figure it out at
! // runtime.
! typep = ((type_T **)stack->ga_data) + stack->ga_len
! - (is_slice ? 3 : 2);
! vtype = (*typep)->tt_type;
! valtype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
! // If the index is a string, the variable must be a Dict.
! if (*typep == &t_any && valtype == &t_string)
! vtype = VAR_DICT;
! if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB)
! {
! if (need_type(valtype, &t_number, -1, 0, cctx,
! FALSE, FALSE) == FAIL)
! return FAIL;
! if (is_slice)
! {
! valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2];
! if (need_type(valtype, &t_number, -2, 0, cctx,
! FALSE, FALSE) == FAIL)
! return FAIL;
! }
! }
!
! if (vtype == VAR_DICT)
! {
! if (is_slice)
! {
! emsg(_(e_cannot_slice_dictionary));
! return FAIL;
! }
! if ((*typep)->tt_type == VAR_DICT)
! {
! *typep = (*typep)->tt_member;
! if (*typep == &t_unknown)
! // empty dict was used
! *typep = &t_any;
! }
! else
! {
! if (need_type(*typep, &t_dict_any, -2, 0, cctx,
! FALSE, FALSE) == FAIL)
! return FAIL;
! *typep = &t_any;
! }
! if (may_generate_2STRING(-1, cctx) == FAIL)
! return FAIL;
! if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
! return FAIL;
! }
! else if (vtype == VAR_STRING)
! {
! *typep = &t_string;
! if ((is_slice
! ? generate_instr_drop(cctx, ISN_STRSLICE, 2)
! : generate_instr_drop(cctx, ISN_STRINDEX, 1)) == FAIL)
! return FAIL;
! }
! else if (vtype == VAR_BLOB)
! {
! emsg("Sorry, blob index and slice not implemented yet");
return FAIL;
- }
- else if (vtype == VAR_LIST || *typep == &t_any)
- {
- if (is_slice)
- {
- if (generate_instr_drop(cctx,
- vtype == VAR_LIST ? ISN_LISTSLICE : ISN_ANYSLICE,
- 2) == FAIL)
- return FAIL;
- }
- else
- {
- if ((*typep)->tt_type == VAR_LIST)
- {
- *typep = (*typep)->tt_member;
- if (*typep == &t_unknown)
- // empty list was used
- *typep = &t_any;
- }
- if (generate_instr_drop(cctx,
- vtype == VAR_LIST ? ISN_LISTINDEX : ISN_ANYINDEX,
- 1) == FAIL)
- return FAIL;
- }
- }
- else
- {
- emsg(_(e_string_list_dict_or_blob_required));
- return FAIL;
- }
}
else if (*p == '.' && p[1] != '.')
{
--- 4106,4113 ----
}
*arg = *arg + 1;
! if (compile_member(is_slice, cctx) == FAIL)
return FAIL;
}
else if (*p == '.' && p[1] != '.')
{
***************
*** 5905,5913 ****
lhs->lhs_type = lhs->lhs_lvar->lv_type;
}
! if (oplen == 3 && !heredoc && lhs->lhs_dest != dest_global
! && lhs->lhs_type->tt_type != VAR_STRING
! && lhs->lhs_type->tt_type != VAR_ANY)
{
emsg(_(e_can_only_concatenate_to_string));
return FAIL;
--- 5916,5926 ----
lhs->lhs_type = lhs->lhs_lvar->lv_type;
}
! if (oplen == 3 && !heredoc
! && lhs->lhs_dest != dest_global
! && !lhs->lhs_has_index
! && lhs->lhs_type->tt_type != VAR_STRING
! && lhs->lhs_type->tt_type != VAR_ANY)
{
emsg(_(e_can_only_concatenate_to_string));
return FAIL;
***************
*** 5993,6018 ****
}
/*
! * Assignment to a list or dict member, or ":unlet" for the item, using the
! * information in "lhs".
! * Returns OK or FAIL.
*/
static int
! compile_assign_unlet(
char_u *var_start,
lhs_T *lhs,
int is_assign,
! type_T *rhs_type,
cctx_T *cctx)
{
- char_u *p;
- int r;
- vartype_T dest_type;
size_t varlen = lhs->lhs_varlen;
! garray_T *stack = &cctx->ctx_type_stack;
! int range = FALSE;
- // Compile the "idx" in "var[idx]" or "key" in "var.key".
p = var_start + varlen;
if (*p == '[')
{
--- 6006,6026 ----
}
/*
! * For an assignment with an index, compile the "idx" in "var[idx]" or "key" in
! * "var.key".
*/
static int
! compile_assign_index(
char_u *var_start,
lhs_T *lhs,
int is_assign,
! int *range,
cctx_T *cctx)
{
size_t varlen = lhs->lhs_varlen;
! char_u *p;
! int r = OK;
p = var_start + varlen;
if (*p == '[')
{
***************
*** 6027,6033 ****
semsg(_(e_cannot_use_range_with_assignment_str), p);
return FAIL;
}
! range = TRUE;
p = skipwhite(p);
if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
{
--- 6035,6041 ----
semsg(_(e_cannot_use_range_with_assignment_str), p);
return FAIL;
}
! *range = TRUE;
p = skipwhite(p);
if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
{
***************
*** 6053,6059 ****
r = generate_PUSHS(cctx, key);
}
! if (r == FAIL)
return FAIL;
if (lhs->lhs_type == &t_any)
--- 6061,6089 ----
r = generate_PUSHS(cctx, key);
}
! return r;
! }
!
! /*
! * Assignment to a list or dict member, or ":unlet" for the item, using the
! * information in "lhs".
! * Returns OK or FAIL.
! */
! static int
! compile_assign_unlet(
! char_u *var_start,
! lhs_T *lhs,
! int is_assign,
! type_T *rhs_type,
! cctx_T *cctx)
! {
! char_u *p;
! vartype_T dest_type;
! size_t varlen = lhs->lhs_varlen;
! garray_T *stack = &cctx->ctx_type_stack;
! int range = FALSE;
!
! if (compile_assign_index(var_start, lhs, is_assign, &range, cctx) == FAIL)
return FAIL;
if (lhs->lhs_type == &t_any)
***************
*** 6330,6338 ****
if (lhs.lhs_has_index)
{
! // TODO: get member from list or dict
! emsg("Index with operation not supported yet");
! goto theend;
}
}
--- 6360,6376 ----
if (lhs.lhs_has_index)
{
! int range = FALSE;
!
! // Get member from list or dict. First compile the
! // index value.
! if (compile_assign_index(var_start, &lhs,
! TRUE, &range, cctx) == FAIL)
! goto theend;
!
! // Get the member.
! if (compile_member(FALSE, cctx) == FAIL)
! goto theend;
}
}
*** ../vim-8.2.2718/src/testdir/test_vim9_assign.vim 2021-04-03 21:00:57.668184656 +0200
--- src/testdir/test_vim9_assign.vim 2021-04-05 17:09:23.738039270 +0200
***************
*** 1116,1122 ****
def Test_assign_dict_with_op()
var lines =<< trim END
- vim9script
var ds: dict<string> = {a: 'x'}
ds['a'] ..= 'y'
ds.a ..= 'z'
--- 1116,1121 ----
***************
*** 1148,1155 ****
dn.a %= 6
assert_equal(2, dn.a)
END
! # TODO: this should also work with a :def function
! CheckScriptSuccess(lines)
enddef
def Test_assign_lambda()
--- 1147,1192 ----
dn.a %= 6
assert_equal(2, dn.a)
END
! CheckDefAndScriptSuccess(lines)
! enddef
!
! def Test_assign_list_with_op()
! var lines =<< trim END
! var ls: list<string> = ['x']
! ls[0] ..= 'y'
! assert_equal('xy', ls[0])
!
! var ln: list<number> = [9]
! ln[0] += 2
! assert_equal(11, ln[0])
!
! ln[0] -= 3
! assert_equal(8, ln[0])
!
! ln[0] *= 2
! assert_equal(16, ln[0])
!
! ln[0] /= 3
! assert_equal(5, ln[0])
!
! ln[0] %= 3
! assert_equal(2, ln[0])
! END
! CheckDefAndScriptSuccess(lines)
! enddef
!
! def Test_assign_with_op_fails()
! var lines =<< trim END
! var s = 'abc'
! s[1] += 'x'
! END
! CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2)
!
! lines =<< trim END
! var s = 'abc'
! s[1] ..= 'x'
! END
! CheckDefAndScriptFailure2(lines, 'E1141:', 'E689:', 2)
enddef
def Test_assign_lambda()
*** ../vim-8.2.2718/src/version.c 2021-04-05 15:38:47.857581963 +0200
--- src/version.c 2021-04-05 15:42:34.036987624 +0200
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 2719,
/**/
--
hundred-and-one symptoms of being an internet addict:
62. If your doorbell rings, you think that new mail has arrived. And then
you're disappointed that it's only someone at the door.
/// Bram Moolenaar -- Br...@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 ///