]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1724: vim9class constructor argument type checking bug v9.0.1724
authorh-east <h.east.727@gmail.com>
Wed, 16 Aug 2023 12:49:54 +0000 (21:49 +0900)
committerChristian Brabandt <cb@256bit.org>
Thu, 17 Aug 2023 20:31:10 +0000 (22:31 +0200)
Problem: vim9class constructor argument type checking bug
Solution: fix it

closes: #12816

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: h-east <h.east.727@gmail.com>
src/proto/vim9instr.pro
src/testdir/test_vim9_class.vim
src/version.c
src/vim9expr.c
src/vim9instr.c

index 9372b1fc67c917ea8d3456875ff580f902602399..6850948289e84a2526feb653851ae78946b946d2 100644 (file)
@@ -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);
index e60b3b63cc597c1f1f324bb05e440522f56e1236..cd67b54153897f036d3772cb6baf6f1ab4fe25c1 100644 (file)
@@ -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
index de1d6e01facb3bab1cd8f8b41d161e2e7d050f61..0d898692b2fc58327cb80aeba2ff82113d075e86 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1724,
 /**/
     1723,
 /**/
index d600cb0ae1a62a41e46367269879ad298762fe26..db4cee1b16a81c451cc03728c185211fa32bbae5 100644 (file)
@@ -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;
     }
 
index ed99cb3f1b5c188296804b2527b3d1e7a80fb5b0..941adf8897c99dab3f1ec29249473893de161bea 100644 (file)
@@ -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)