patch 9.0.1724: vim9class constructor argument type checking bug

Commit: 
https://github.com/vim/vim/commit/2261c89a49ff2115e1ccc9ab9211e9f0d5a37578
Author: h-east <[email protected]>
Date:   Wed Aug 16 21:49:54 2023 +0900

    patch 9.0.1724: vim9class constructor argument type checking bug
    
    Problem: vim9class constructor argument type checking bug
    Solution: fix it
    
    closes: #12816
    
    Signed-off-by: Christian Brabandt <[email protected]>
    Co-authored-by: h-east <[email protected]>

diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro
index 9372b1fc6..685094828 100644
--- a/src/proto/vim9instr.pro
+++ b/src/proto/vim9instr.pro
@@ -57,7 +57,7 @@ int check_internal_func_args(cctx_T *cctx, int func_idx, int 
argcount, int metho
 int generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call);
 int generate_LISTAPPEND(cctx_T *cctx);
 int generate_BLOBAPPEND(cctx_T *cctx);
-int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int mi, int 
pushed_argcount);
+int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int mi, type_T 
*mtype, int pushed_argcount);
 int generate_UCALL(cctx_T *cctx, char_u *name, int argcount);
 int check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int 
at_top, char_u *name);
 int generate_PCALL(cctx_T *cctx, int argcount, char_u *name, type_T *type, int 
at_top);
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index e60b3b63c..cd67b5415 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -518,6 +518,83 @@ def Test_class_default_new()
   v9.CheckScriptFailure(lines, 'E119:')
 enddef
 
+
+def Test_class_new_with_object_member()
+  var lines =<< trim END
+      vim9script
+
+      class C
+        this.str: string
+        this.num: number
+        def new(this.str, this.num)
+        enddef
+        def newVals(this.str, this.num)
+        enddef
+      endclass
+
+      def Check()
+        try
+          var c = C.new('cats', 2)
+          assert_equal('cats', c.str)
+          assert_equal(2, c.num)
+
+          c = C.newVals('dogs', 4)
+          assert_equal('dogs', c.str)
+          assert_equal(4, c.num)
+        catch
+          assert_report($'Unexpected exception was caught: {v:exception}')
+        endtry
+      enddef
+
+      Check()
+  END
+  v9.CheckScriptSuccess(lines)
+
+  lines =<< trim END
+      vim9script
+
+      class C
+        this.str: string
+        this.num: number
+        def new(this.str, this.num)
+        enddef
+      endclass
+
+      def Check()
+        try
+          var c = C.new(1, 2)
+        catch
+          assert_report($'Unexpected exception was caught: {v:exception}')
+        endtry
+      enddef
+
+      Check()
+  END
+  v9.CheckScriptFailure(lines, 'E1013:')
+
+  lines =<< trim END
+      vim9script
+
+      class C
+        this.str: string
+        this.num: number
+        def newVals(this.str, this.num)
+        enddef
+      endclass
+
+      def Check()
+        try
+          var c = C.newVals('dogs', 'apes')
+        catch
+          assert_report($'Unexpected exception was caught: {v:exception}')
+        endtry
+      enddef
+
+      Check()
+  END
+  v9.CheckScriptFailure(lines, 'E1013:')
+enddef
+
 def Test_class_object_member_inits()
   var lines =<< trim END
       vim9script
diff --git a/src/version.c b/src/version.c
index de1d6e01f..0d898692b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1724,
 /**/
     1723,
 /**/
diff --git a/src/vim9expr.c b/src/vim9expr.c
index d600cb0ae..db4cee1b1 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -358,8 +358,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, 
type_T *type)
 
        if (type->tt_type == VAR_OBJECT
                     && (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
-           return generate_CALL(cctx, ufunc, cl, fi, argcount);
-       return generate_CALL(cctx, ufunc, NULL, 0, argcount);
+           return generate_CALL(cctx, ufunc, cl, fi, type, argcount);
+       return generate_CALL(cctx, ufunc, NULL, 0, type, argcount);
     }
 
     if (type->tt_type == VAR_OBJECT)
@@ -932,6 +932,7 @@ compile_call(
     int                has_g_namespace;
     ca_special_T special_fn;
     imported_T *import;
+    type_T     *type;
 
     if (varlen >= sizeof(namebuf))
     {
@@ -1015,6 +1016,7 @@ compile_call(
     if (compile_arguments(arg, cctx, &argcount, special_fn) == FAIL)
        goto theend;
 
+    type = get_decl_type_on_stack(cctx, 1);
     is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL;
     if (ASCII_ISLOWER(*name) && name[1] != ':' && !is_autoload)
     {
@@ -1032,8 +1034,6 @@ compile_call(
 
            if (STRCMP(name, "add") == 0 && argcount == 2)
            {
-               type_T      *type = get_decl_type_on_stack(cctx, 1);
-
                // add() can be compiled to instructions if we know the type
                if (type->tt_type == VAR_LIST)
                {
@@ -1080,7 +1080,7 @@ compile_call(
        {
            if (!func_is_global(ufunc))
            {
-               res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
+               res = generate_CALL(cctx, ufunc, NULL, 0, type, argcount);
                goto theend;
            }
            if (!has_g_namespace
@@ -1109,7 +1109,7 @@ compile_call(
     // If we can find a global function by name generate the right call.
     if (ufunc != NULL)
     {
-       res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
+       res = generate_CALL(cctx, ufunc, NULL, 0, type, argcount);
        goto theend;
     }
 
diff --git a/src/vim9instr.c b/src/vim9instr.c
index ed99cb3f1..941adf889 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -1780,6 +1780,7 @@ generate_CALL(
        ufunc_T     *ufunc,
        class_T     *cl,
        int         mi,
+       type_T      *mtype,     // method type
        int         pushed_argcount)
 {
     isn_T      *isn;
@@ -1805,6 +1806,8 @@ generate_CALL(
     {
        int             i;
        compiletype_T   compile_type;
+       int             class_constructor = (mtype->tt_type == VAR_CLASS
+                                   && STRNCMP(ufunc->uf_name, "new", 3) == 0);
 
        for (i = 0; i < argcount; ++i)
        {
@@ -1823,6 +1826,25 @@ generate_CALL(
                if (ufunc->uf_arg_types == NULL)
                    continue;
                expected = ufunc->uf_arg_types[i];
+
+               // When the method is a class constructor and the formal
+               // argument is an object member, the type check is performed on
+               // the object member type.
+               if (class_constructor && expected->tt_type == VAR_ANY)
+               {
+                   class_T *clp = mtype->tt_class;
+                   char_u *aname = ((char_u **)ufunc->uf_args.ga_data)[i];
+                   for (int om = 0; om < clp->class_obj_member_count; ++om)
+                   {
+                       if (STRCMP(aname, clp->class_obj_members[om].ocm_name)
+                                                                       == 0)
+                       {
+                           expected = clp->class_obj_members[om].ocm_type;
+                           break;
+                       }
+                   }
+
+               }
            }
            else if (ufunc->uf_va_type == NULL
                                           || ufunc->uf_va_type == &t_list_any)

-- 
-- 
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 on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1qWjrb-006Ycs-Az%40256bit.org.

Raspunde prin e-mail lui