Commit: patch 9.2.0975: Vim9: assignment to a member of an object member fails

Christian Brabandt <[email protected]>
Newsgroups gmane.editors.vim.devel
Message-ID <[email protected]>
patch 9.2.0975: Vim9: assignment to a member of an object member fails

Commit: https://github.com/vim/vim/commit/44a92018a5a9e6d947845c748ab3add7b2d8e7c3
Author: Hirohito Higashi <[email protected]>
Date:   Tue Aug 18 20:50:15 2026 +0000

    patch 9.2.0975: Vim9: assignment to a member of an object member fails
    
    Problem:  Using an operator on a member of an object that is itself a
              member of an object gives E909, and a plain assignment to it
              stores the value in the wrong member (Mao-Yining).
    Solution: When the last index is split off from the name, forget the
              member index found for the first name.  The index is compiled
              before the variable, thus do not use the type that loading the
              variable produced.  Load the member of an object like an
              expression does instead of indexing it (Hirohito Higashi).
    
    fixes:  #21084
    closes: #21085
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
    Signed-off-by: Hirohito Higashi <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/src/proto/vim9expr.pro b/src/proto/vim9expr.pro
index 4de143917..055723ce4 100644
--- a/src/proto/vim9expr.pro
+++ b/src/proto/vim9expr.pro
@@ -2,6 +2,7 @@
 int generate_ppconst(cctx_T *cctx, ppconst_T *ppconst);
 void clear_ppconst(ppconst_T *ppconst);
 int compile_member(int is_slice, int *keeping_dict, cctx_T *cctx);
+int compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type);
 int compile_load_scriptvar(cctx_T *cctx, char_u *name, char_u *start, char_u **end, imported_T *import);
 int compile_load(char_u **arg, size_t namelen, char_u *end_arg, cctx_T *cctx, int is_expr, int error);
 int compile_arguments(char_u **arg, cctx_T *cctx, int *argcount, ca_special_T special_fn);
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index eb108bcb9..5875f6426 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -11904,4 +11904,65 @@ def Test_class_member_closure()
   v9.CheckSourceSuccess(lines)
 enddef
 
+" Using a compound operator on an object variable of an object variable.
+def Test_nested_object_member_op_assign()
+  var lines =<< trim END
+    vim9script
+    class A
+      public var n = 1
+      public var s = 'x'
+    endclass
+    class B
+      var a: A = A.new()
+      def Add()
+        this.a.n += 2
+        this.a.s ..= 'y'
+      enddef
+    endclass
+    var b = B.new()
+    b.Add()
+    assert_equal(3, b.a.n)
+    assert_equal('xy', b.a.s)
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # Also when the object is in a local variable.
+  lines =<< trim END
+    vim9script
+    class A
+      public var n = 1
+    endclass
+    class B
+      var a: A = A.new()
+    endclass
+    def F(): number
+      var b = B.new()
+      b.a.n += 2
+      return b.a.n
+    enddef
+    assert_equal(3, F())
+  END
+  v9.CheckSourceSuccess(lines)
+
+  # A plain assignment must use the last name, not the first one.
+  lines =<< trim END
+    vim9script
+    class A
+      public var n = 1
+      public var s = 'x'
+    endclass
+    class B
+      var a: A = A.new()
+      def Set()
+        this.a.s = 'y'
+      enddef
+    endclass
+    var b = B.new()
+    b.Set()
+    assert_equal(1, b.a.n)
+    assert_equal('y', b.a.s)
+  END
+  v9.CheckSourceSuccess(lines)
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 2341e6991..fcb088bdb 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    975,
 /**/
     974,
 /**/
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 2aeef4095..5f51fdf6d 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2265,8 +2265,10 @@ compile_lhs_set_member_type(
 	lhs->lhs_varlen = after - var_start;
 	lhs->lhs_dest = dest_expr;
 	// We don't know the type before evaluating the expression,
-	// use "any" until then.
+	// use "any" until then.  The member index is for the first name,
+	// not for the last index.
 	lhs->lhs_type = &t_any;
+	lhs->lhs_member_idx = -1;
     }
 
     int use_class = lhs->lhs_type != NULL
@@ -2623,7 +2625,17 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
 
     if (lhs->lhs_has_index)
     {
-	int range = FALSE;
+	int	range = FALSE;
+	type_T	*type = get_type_on_stack(cctx, 0);
+
+	// A member of an object or class is not obtained by indexing it.
+	if (type->tt_type == VAR_CLASS
+		|| (type->tt_type == VAR_OBJECT && type != &t_object_any))
+	{
+	    char_u *p = var_start + lhs->lhs_varlen;
+
+	    return compile_class_object_index(cctx, &p, type);
+	}
 
 	// Get member from list or dict.  First compile the
 	// index value.
@@ -2673,7 +2685,10 @@ compile_assign_unlet(
 	return FAIL;
     }
 
-    if (lhs->lhs_type == NULL || lhs->lhs_type == &t_any)
+    // For "expr[idx]" the index is compiled before the expression, thus the
+    // resulting type cannot be used here.
+    if (lhs->lhs_dest == dest_expr
+	    || lhs->lhs_type == NULL || lhs->lhs_type == &t_any)
     {
 	// Index on variable of unknown type: check at runtime.
 	dest_type = VAR_ANY;
diff --git a/src/vim9expr.c b/src/vim9expr.c
index 7f47d82fb..87a872f58 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -347,7 +347,7 @@ inside_class_hierarchy(cctx_T *cctx_arg, class_T *cl)
 /*
  * Compile ".member" coming after an object or class.
  */
-    static int
+    int
 compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
 {
     int		m_idx;

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups "vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1wwQuk-00AkBl-VI%40256bit.org.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.