patch 9.0.1928: Vim9: constructor type checking bug

Commit: 
https://github.com/vim/vim/commit/b895b0fabce7d952a6617eb69fc1e1597ece8b00
Author: h-east <h.east....@gmail.com>
Date:   Sun Sep 24 15:46:31 2023 +0200

    patch 9.0.1928: Vim9: constructor type checking bug
    
    Problem:  Vim9: constructor type checking bug
    Solution: Fix class constructor regression
    
    closes: #13102
    closes: #13113
    
    Signed-off-by: Christian Brabandt <c...@256bit.org>
    Co-authored-by: h-east <h.east....@gmail.com>

diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index e5d78b66f..7b4129b47 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -47,7 +47,7 @@ char_u *get_scriptlocal_funcname(char_u *funcname);
 char_u *alloc_printable_func_name(char_u *fname);
 char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, 
funcdict_T *fudi);
 void list_functions(regmatch_T *regmatch);
-ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T 
*lines_to_free, int class_flags);
+ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T 
*lines_to_free, int class_flags, ocmember_T *obj_members, int obj_member_count);
 void ex_function(exarg_T *eap);
 ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
 void ex_defcompile(exarg_T *eap);
diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro
index 3f287d064..aef934f57 100644
--- a/src/proto/vim9instr.pro
+++ b/src/proto/vim9instr.pro
@@ -58,7 +58,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, type_T 
*mtype, int pushed_argcount);
+int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int mi, 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 6c338167f..87679d814 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -921,6 +921,27 @@ def Test_class_new_with_object_member()
       Check()
   END
   v9.CheckSourceFailure(lines, 'E1013:')
+
+  lines =<< trim END
+      vim9script
+
+      class C
+        this.str: string
+        def new(str: any)
+        enddef
+      endclass
+
+      def Check()
+        try
+          var c = C.new(1)
+        catch
+          assert_report($'Unexpected exception was caught: {v:exception}')
+        endtry
+      enddef
+
+      Check()
+  END
+  v9.CheckSourceSuccess(lines)
 enddef
 
 def Test_class_object_member_inits()
diff --git a/src/userfunc.c b/src/userfunc.c
index efde29dcb..a27ff984d 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -65,6 +65,7 @@ one_function_arg(
        garray_T    *newargs,
        garray_T    *argtypes,
        int         types_optional,
+       garray_T    *arg_objm,
        evalarg_T   *evalarg,
        exarg_T     *eap,
        int         is_vararg,
@@ -136,7 +137,8 @@ one_function_arg(
     }
 
     // get any type from "arg: type"
-    if (argtypes != NULL && (skip || ga_grow(argtypes, 1) == OK))
+    if (argtypes != NULL && (skip || ga_grow(argtypes, 1) == OK)
+               && arg_objm != NULL && (skip || ga_grow(arg_objm, 1) == OK))
     {
        char_u *type = NULL;
 
@@ -172,6 +174,7 @@ one_function_arg(
                type = vim_strsave((char_u *)
                                            (is_vararg ? "list<any>" : "any"));
            ((char_u **)argtypes->ga_data)[argtypes->ga_len++] = type;
+           ((int8_T *)arg_objm->ga_data)[arg_objm->ga_len++] = FALSE;
        }
     }
 
@@ -221,6 +224,7 @@ get_function_args(
     garray_T   *newargs,
     garray_T   *argtypes,      // NULL unless using :def
     int                types_optional, // types optional if "argtypes" is not 
NULL
+    garray_T   *arg_objm,      // NULL unless using :def
     evalarg_T  *evalarg,       // context or NULL
     int                *varargs,
     garray_T   *default_args,
@@ -241,6 +245,8 @@ get_function_args(
        ga_init2(newargs, sizeof(char_u *), 3);
     if (argtypes != NULL)
        ga_init2(argtypes, sizeof(char_u *), 3);
+    if (arg_objm != NULL)
+       ga_init2(arg_objm, sizeof(int8_T), 3);
     if (!skip && default_args != NULL)
        ga_init2(default_args, sizeof(char_u *), 3);
 
@@ -295,7 +301,7 @@ get_function_args(
 
                arg = p;
                p = one_function_arg(p, newargs, argtypes, types_optional,
-                                                    evalarg, eap, TRUE, skip);
+                                       arg_objm, evalarg, eap, TRUE, skip);
                if (p == arg)
                    break;
                if (*skipwhite(p) == '=')
@@ -350,11 +356,13 @@ get_function_args(
                                               vim_strnsave(arg, argend - arg);
                newargs->ga_len++;
 
-               if (argtypes != NULL && ga_grow(argtypes, 1) == OK)
+               if (argtypes != NULL && ga_grow(argtypes, 1) == OK
+                           && arg_objm != NULL && ga_grow(arg_objm, 1) == OK)
                {
                    // TODO: use the actual type
                    ((char_u **)argtypes->ga_data)[argtypes->ga_len++] =
                                                  vim_strsave((char_u *)"any");
+                   ((int8_T *)arg_objm->ga_data)[arg_objm->ga_len++] = TRUE;
 
                    // Add a line to the function body for the assignment.
                    if (ga_grow(newlines, 1) == OK)
@@ -391,7 +399,7 @@ get_function_args(
 
            arg = p;
            p = one_function_arg(p, newargs, argtypes, types_optional,
-                                                   evalarg, eap, FALSE, skip);
+                                       arg_objm, evalarg, eap, FALSE, skip);
            if (p == arg)
                break;
 
@@ -490,7 +498,13 @@ err_ret:
  * Return OK or FAIL.
  */
     static int
-parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs)
+parse_argument_types(
+       ufunc_T *fp,
+       garray_T *argtypes,
+       int varargs,
+       garray_T *arg_objm,
+       ocmember_T *obj_members,
+       int obj_member_count)
 {
     int len = 0;
 
@@ -516,7 +530,24 @@ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int 
varargs)
                    // will get the type from the default value
                    type = &t_unknown;
                else
-                   type = parse_type(&p, &fp->uf_type_list, TRUE);
+               {
+                   if (arg_objm != NULL && ((int8_T *)arg_objm->ga_data)[i])
+                   {
+                       char_u *aname = ((char_u **)fp->uf_args.ga_data)[i];
+
+                       type = &t_any;
+                       for (int om = 0; om < obj_member_count; ++om)
+                       {
+                           if (STRCMP(aname, obj_members[om].ocm_name) == 0)
+                           {
+                               type = obj_members[om].ocm_type;
+                               break;
+                           }
+                       }
+                   }
+                   else
+                       type = parse_type(&p, &fp->uf_type_list, TRUE);
+               }
                if (type == NULL)
                    return FAIL;
                fp->uf_arg_types[i] = type;
@@ -1395,7 +1426,7 @@ lambda_function_body(
     SOURCING_LNUM = sourcing_lnum_top;
 
     // parse argument types
-    if (parse_argument_types(ufunc, argtypes, varargs) == FAIL)
+    if (parse_argument_types(ufunc, argtypes, varargs, NULL, NULL, 0) == FAIL)
     {
        SOURCING_LNUM = lnum_save;
        goto erret;
@@ -1464,6 +1495,7 @@ get_lambda_tv(
     garray_T   *pnewargs;
     garray_T   argtypes;
     garray_T   default_args;
+    garray_T   arg_objm;
     ufunc_T    *fp = NULL;
     partial_T   *pt = NULL;
     int                varargs;
@@ -1490,12 +1522,16 @@ get_lambda_tv(
     // be found after the arguments.
     s = *arg + 1;
     ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
-           types_optional ? &argtypes : NULL, types_optional, evalarg,
-                          NULL, &default_args, TRUE, NULL, FALSE, NULL, NULL);
+           types_optional ? &argtypes : NULL, types_optional,
+                       types_optional ? &arg_objm : NULL, evalarg,
+                       NULL, &default_args, TRUE, NULL, FALSE, NULL, NULL);
     if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL)
     {
        if (types_optional)
+       {
            ga_clear_strings(&argtypes);
+           ga_clear(&arg_objm);
+       }
        return called_emsg == called_emsg_start ? NOTDONE : FAIL;
     }
 
@@ -1506,7 +1542,8 @@ get_lambda_tv(
        pnewargs = NULL;
     *arg += 1;
     ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
-           types_optional ? &argtypes : NULL, types_optional, evalarg,
+           types_optional ? &argtypes : NULL, types_optional,
+                       types_optional ? &arg_objm : NULL, evalarg,
                                            &varargs, &default_args,
                                            FALSE, NULL, FALSE, NULL, NULL);
     if (ret == FAIL
@@ -1514,7 +1551,10 @@ get_lambda_tv(
                equal_arrow || vim9script ? &white_error : NULL)) == NULL)
     {
        if (types_optional)
+       {
            ga_clear_strings(&argtypes);
+           ga_clear(&arg_objm);
+       }
        ga_clear_strings(&newargs);
        return white_error ? FAIL : NOTDONE;
     }
@@ -1631,7 +1671,7 @@ get_lambda_tv(
        if (types_optional)
        {
            if (parse_argument_types(fp, &argtypes,
-                                               vim9script && varargs) == FAIL)
+                               vim9script && varargs, NULL, NULL, 0) == FAIL)
                goto errret;
            if (ret_type != NULL)
            {
@@ -4591,7 +4631,9 @@ define_function(
        exarg_T     *eap,
        char_u      *name_arg,
        garray_T    *lines_to_free,
-       int         class_flags)
+       int         class_flags,
+       ocmember_T  *obj_members,
+       int         obj_member_count)
 {
     int                j;
     int                c;
@@ -4604,6 +4646,7 @@ define_function(
     char_u     *line_arg = NULL;
     garray_T   newargs;
     garray_T   argtypes;
+    garray_T   arg_objm;
     garray_T   default_args;
     garray_T   newlines;
     int                varargs = FALSE;
@@ -4662,6 +4705,7 @@ define_function(
 
     ga_init(&newargs);
     ga_init(&argtypes);
+    ga_init(&arg_objm);
     ga_init(&default_args);
 
     /*
@@ -4909,6 +4953,7 @@ define_function(
     ++p;
     if (get_function_args(&p, ')', &newargs,
                         eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
+                        eap->cmdidx == CMD_def ? &arg_objm : NULL,
                         NULL, &varargs, &default_args, eap->skip,
                         eap, class_flags, &newlines, lines_to_free) == FAIL)
        goto errret_2;
@@ -5252,7 +5297,8 @@ define_function(
        // The function may use script variables from the context.
        function_using_block_scopes(fp, cstack);
 
-       if (parse_argument_types(fp, &argtypes, varargs) == FAIL)
+       if (parse_argument_types(fp, &argtypes, varargs, &arg_objm,
+                                       obj_members, obj_member_count) == FAIL)
        {
            SOURCING_LNUM = lnum_save;
            free_fp = fp_allocated;
@@ -5352,6 +5398,7 @@ errret_2:
        VIM_CLEAR(fp);
 ret_free:
     ga_clear_strings(&argtypes);
+    ga_clear(&arg_objm);
     vim_free(fudi.fd_newkey);
     if (name != name_arg)
        vim_free(name);
@@ -5370,7 +5417,7 @@ ex_function(exarg_T *eap)
     garray_T lines_to_free;
 
     ga_init2(&lines_to_free, sizeof(char_u *), 50);
-    (void)define_function(eap, NULL, &lines_to_free, 0);
+    (void)define_function(eap, NULL, &lines_to_free, 0, NULL, 0);
     ga_clear_strings(&lines_to_free);
 }
 
diff --git a/src/version.c b/src/version.c
index 3d663e1a1..574dedfbc 100644
--- a/src/version.c
+++ b/src/version.c
@@ -699,6 +699,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1928,
 /**/
     1927,
 /**/
diff --git a/src/vim9class.c b/src/vim9class.c
index a2e305c0a..0b9ded747 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -1200,7 +1200,8 @@ add_default_constructor(
     garray_T lines_to_free;
     ga_init2(&lines_to_free, sizeof(char_u *), 50);
 
-    ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
+    ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS,
+                           cl->class_obj_members, cl->class_obj_member_count);
 
     ga_clear_strings(&lines_to_free);
     vim_free(fga.ga_data);
@@ -1679,7 +1680,7 @@ early_ret:
            else
                class_flags = CF_INTERFACE;
            ufunc_T *uf = define_function(&ea, NULL, &lines_to_free,
-                                                               class_flags);
+                       class_flags, objmembers.ga_data, objmembers.ga_len);
            ga_clear_strings(&lines_to_free);
 
            if (uf != NULL)
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 16898e32c..c69afb999 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1101,7 +1101,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, 
garray_T *lines_to_free)
     int save_KeyTyped = KeyTyped;
     KeyTyped = FALSE;
 
-    ufunc = define_function(eap, lambda_name, lines_to_free, 0);
+    ufunc = define_function(eap, lambda_name, lines_to_free, 0, NULL, 0);
 
     KeyTyped = save_KeyTyped;
 
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 23dee1cfc..a53b1b3bf 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -4470,7 +4470,7 @@ exec_instructions(ectx_T *ectx)
                    ea.cmd = ea.arg = iptr->isn_arg.string;
                    ga_init2(&lines_to_free, sizeof(char_u *), 50);
                    SOURCING_LNUM = iptr->isn_lnum;
-                   define_function(&ea, NULL, &lines_to_free, 0);
+                   define_function(&ea, NULL, &lines_to_free, 0, NULL, 0);
                    ga_clear_strings(&lines_to_free);
                }
                break;
diff --git a/src/vim9expr.c b/src/vim9expr.c
index bcad79ca1..20289bf8b 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -395,8 +395,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, type, argcount);
-       return generate_CALL(cctx, ufunc, NULL, 0, type, argcount);
+           return generate_CALL(cctx, ufunc, cl, fi, argcount);
+       return generate_CALL(cctx, ufunc, NULL, 0, argcount);
     }
 
     if (type->tt_type == VAR_OBJECT)
@@ -977,7 +977,6 @@ compile_call(
     int                has_g_namespace;
     ca_special_T special_fn;
     imported_T *import;
-    type_T     *type;
 
     if (varlen >= sizeof(namebuf))
     {
@@ -1061,7 +1060,6 @@ 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)
     {
@@ -1079,6 +1077,8 @@ 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)
                {
@@ -1128,7 +1128,7 @@ compile_call(
        {
            if (!func_is_global(ufunc))
            {
-               res = generate_CALL(cctx, ufunc, NULL, 0, type, argcount);
+               res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
                goto theend;
            }
            if (!has_g_namespace
@@ -1147,7 +1147,7 @@ compile_call(
            if (cctx->ctx_ufunc->uf_defclass == cl)
            {
                res = generate_CALL(cctx, cl->class_class_functions[mi], NULL,
-                                                       0, type, argcount);
+                                                       0, argcount);
            }
            else
            {
@@ -1175,7 +1175,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, type, argcount);
+       res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
        goto theend;
     }
 
diff --git a/src/vim9instr.c b/src/vim9instr.c
index b9bbfd09b..96c02de7c 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -1784,7 +1784,6 @@ generate_CALL(
        ufunc_T     *ufunc,
        class_T     *cl,
        int         mi,
-       type_T      *mtype,     // method type
        int         pushed_argcount)
 {
     isn_T      *isn;
@@ -1810,8 +1809,6 @@ 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)
        {
@@ -1830,18 +1827,6 @@ 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];
-                   ocmember_T *m = object_member_lookup(clp, aname, 0, NULL);
-                   if (m != NULL)
-                       expected = m->ocm_type;
-               }
            }
            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 vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1qkPea-009Wjq-Dt%40256bit.org.

Raspunde prin e-mail lui