changeset 32822:b3a42579bb3f v9.0.1724

patch 9.0.1724: vim9class constructor argument type checking bug Commit: https://github.com/vim/vim/commit/2261c89a49ff2115e1ccc9ab9211e9f0d5a37578 Author: h-east <h.east.727@gmail.com> 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 <cb@256bit.org> Co-authored-by: h-east <h.east.727@gmail.com>
author Christian Brabandt <cb@256bit.org>
date Thu, 17 Aug 2023 22:45:03 +0200
parents 52a61f786e78
children d18b744d834b
files src/proto/vim9instr.pro src/testdir/test_vim9_class.vim src/version.c src/vim9expr.c src/vim9instr.c
diffstat 5 files changed, 108 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/proto/vim9instr.pro
+++ b/src/proto/vim9instr.pro
@@ -57,7 +57,7 @@ int check_internal_func_args(cctx_T *cct
 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);
--- 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
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1724,
+/**/
     1723,
 /**/
     1722,
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -358,8 +358,8 @@ compile_class_object_index(cctx_T *cctx,
 
 	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;
     }
 
--- 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)