# HG changeset patch # User Christian Brabandt # Date 1698354906 -7200 # Node ID fcc8296f36eba3dd3520595767dc3c046ea1bfa6 # Parent fbe029687c674048c31daf968dd484adee55d272 patch 9.0.2072: Vim9: no nr2str conversion in list-unpack Commit: https://github.com/vim/vim/commit/c229a6ac0775e07dff456ca8832c516e57a74e74 Author: Yegappan Lakshmanan Date: Thu Oct 26 23:05:07 2023 +0200 patch 9.0.2072: Vim9: no nr2str conversion in list-unpack Problem: Vim9: no nr2str conversion in list-unpack Solution: Generate 2STRING instruction to convert dict index to string Generate instruction to convert dict index to a string fixes: #13417 closes: #13424 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -2986,4 +2986,21 @@ def Test_heredoc_expr() v9.CheckDefAndScriptFailure(lines, 'E15: Invalid expression: "}"') enddef +" Test for assigning to a multi-dimensional list item. +def Test_list_item_assign() + var lines =<< trim END + vim9script + + def Foo() + var l: list> = [['x', 'x', 'x'], ['y', 'y', 'y']] + var z: number = 1 + + [l[1][2], z] = ['a', 20] + assert_equal([['x', 'x', 'x'], ['y', 'y', 'a']], l) + enddef + Foo() + END + v9.CheckSourceSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -8442,4 +8442,133 @@ def Test_class_variable_as_operands() v9.CheckSourceSuccess(lines) enddef +" Test for checking the type of the key used to access an object dict member. +def Test_dict_member_key_type_check() + var lines =<< trim END + vim9script + + abstract class State + this.numbers: dict = {0: 'nil', 1: 'unity'} + endclass + + class Test extends State + def ObjMethodTests() + var cursor: number = 0 + var z: number = 0 + [this.numbers[cursor]] = ['zero.1'] + assert_equal({0: 'zero.1', 1: 'unity'}, this.numbers) + [this.numbers[string(cursor)], z] = ['zero.2', 1] + assert_equal({0: 'zero.2', 1: 'unity'}, this.numbers) + [z, this.numbers[string(cursor)]] = [1, 'zero.3'] + assert_equal({0: 'zero.3', 1: 'unity'}, this.numbers) + [this.numbers[cursor], z] = ['zero.4', 1] + assert_equal({0: 'zero.4', 1: 'unity'}, this.numbers) + [z, this.numbers[cursor]] = [1, 'zero.5'] + assert_equal({0: 'zero.5', 1: 'unity'}, this.numbers) + enddef + + static def ClassMethodTests(that: State) + var cursor: number = 0 + var z: number = 0 + [that.numbers[cursor]] = ['zero.1'] + assert_equal({0: 'zero.1', 1: 'unity'}, that.numbers) + [that.numbers[string(cursor)], z] = ['zero.2', 1] + assert_equal({0: 'zero.2', 1: 'unity'}, that.numbers) + [z, that.numbers[string(cursor)]] = [1, 'zero.3'] + assert_equal({0: 'zero.3', 1: 'unity'}, that.numbers) + [that.numbers[cursor], z] = ['zero.4', 1] + assert_equal({0: 'zero.4', 1: 'unity'}, that.numbers) + [z, that.numbers[cursor]] = [1, 'zero.5'] + assert_equal({0: 'zero.5', 1: 'unity'}, that.numbers) + enddef + + def new() + enddef + + def newMethodTests() + var cursor: number = 0 + var z: number + [this.numbers[cursor]] = ['zero.1'] + assert_equal({0: 'zero.1', 1: 'unity'}, this.numbers) + [this.numbers[string(cursor)], z] = ['zero.2', 1] + assert_equal({0: 'zero.2', 1: 'unity'}, this.numbers) + [z, this.numbers[string(cursor)]] = [1, 'zero.3'] + assert_equal({0: 'zero.3', 1: 'unity'}, this.numbers) + [this.numbers[cursor], z] = ['zero.4', 1] + assert_equal({0: 'zero.4', 1: 'unity'}, this.numbers) + [z, this.numbers[cursor]] = [1, 'zero.5'] + assert_equal({0: 'zero.5', 1: 'unity'}, this.numbers) + enddef + endclass + + def DefFuncTests(that: Test) + var cursor: number = 0 + var z: number + [that.numbers[cursor]] = ['zero.1'] + assert_equal({0: 'zero.1', 1: 'unity'}, that.numbers) + [that.numbers[string(cursor)], z] = ['zero.2', 1] + assert_equal({0: 'zero.2', 1: 'unity'}, that.numbers) + [z, that.numbers[string(cursor)]] = [1, 'zero.3'] + assert_equal({0: 'zero.3', 1: 'unity'}, that.numbers) + [that.numbers[cursor], z] = ['zero.4', 1] + assert_equal({0: 'zero.4', 1: 'unity'}, that.numbers) + [z, that.numbers[cursor]] = [1, 'zero.5'] + assert_equal({0: 'zero.5', 1: 'unity'}, that.numbers) + enddef + + Test.newMethodTests() + Test.new().ObjMethodTests() + Test.ClassMethodTests(Test.new()) + DefFuncTests(Test.new()) + + const test: Test = Test.new() + var cursor: number = 0 + [test.numbers[cursor], cursor] = ['zero', 1] + [cursor, test.numbers[cursor]] = [1, 'one'] + assert_equal({0: 'zero', 1: 'one'}, test.numbers) + END + v9.CheckSourceSuccess(lines) + + lines =<< trim END + vim9script + + class A + this.numbers: dict = {a: '1', b: '2'} + + def new() + enddef + + def Foo() + var z: number + [this.numbers.a, z] = [{}, 10] + enddef + endclass + + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected string but got dict', 2) + + lines =<< trim END + vim9script + + class A + this.numbers: dict = {a: 1, b: 2} + + def new() + enddef + + def Foo() + var x: string = 'a' + var y: number + [this.numbers[x], y] = [{}, 10] + enddef + endclass + + var a = A.new() + a.Foo() + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got dict', 3) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -560,6 +560,7 @@ def Test_disassemble_store_index() '\d LOAD $0\_s*' .. '\d MEMBER dd\_s*' .. '\d\+ USEDICT\_s*' .. + '\d\+ 2STRING stack\[-2\]\_s*' .. '\d\+ STOREINDEX any\_s*' .. '\d\+ RETURN void', res) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2072, +/**/ 2071, /**/ 2070, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2040,9 +2040,7 @@ compile_lhs( lhs->lhs_member_type = m->ocm_type; } else - { lhs->lhs_member_type = lhs->lhs_type->tt_member; - } } return OK; } @@ -2220,16 +2218,27 @@ compile_load_lhs( return FAIL; } + if (lhs->lhs_type->tt_type == VAR_DICT && var_start[varlen] == '[') + { + // If the lhs is a Dict variable and an item is accessed by "[", + // then need to convert the key into a string. The top item in the + // type stack is the Dict and the second last item is the key. + if (may_generate_2STRING(-2, FALSE, cctx) == FAIL) + return FAIL; + } + // Now we can properly check the type. The variable is indexed, thus // we need the member type. For a class or object we don't know the // type yet, it depends on what member is used. + // The top item in the stack is the Dict, followed by the key and then + // the type of the value. vartype_T vartype = lhs->lhs_type->tt_type; type_T *member_type = lhs->lhs_type->tt_member; if (rhs_type != NULL && member_type != NULL && vartype != VAR_OBJECT && vartype != VAR_CLASS && rhs_type != &t_void && need_type(rhs_type, member_type, FALSE, - -2, 0, cctx, FALSE, FALSE) == FAIL) + -3, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } else