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 <cb@256bit.org>
Co-authored-by: h-east <h.east.727@gmail.com>
diff --git a/src/userfunc.c b/src/userfunc.c
index efde29d..a27ff98 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -65,6 +65,7 @@
 	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 @@
     }
 
     // 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 @@
 		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 @@
     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 @@
 	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 @@
 
 		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 @@
 					       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 @@
 
 	    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 @@
  * 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 @@
 		    // 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 @@
     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 @@
     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 @@
     // 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 @@
 	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 @@
 		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 @@
 	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 @@
 	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 @@
     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 @@
 
     ga_init(&newargs);
     ga_init(&argtypes);
+    ga_init(&arg_objm);
     ga_init(&default_args);
 
     /*
@@ -4909,6 +4953,7 @@
     ++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 @@
 	// 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 @@
 	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 @@
     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);
 }