0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

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>
This commit is contained in:
h-east 2023-09-24 15:46:31 +02:00 committed by Christian Brabandt
parent ceee7a808c
commit b895b0fabc
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
10 changed files with 98 additions and 42 deletions

View File

@ -47,7 +47,7 @@ char_u *get_scriptlocal_funcname(char_u *funcname);
char_u *alloc_printable_func_name(char_u *fname); 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); char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
void list_functions(regmatch_T *regmatch); 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); void ex_function(exarg_T *eap);
ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type); ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
void ex_defcompile(exarg_T *eap); void ex_defcompile(exarg_T *eap);

View File

@ -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_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call);
int generate_LISTAPPEND(cctx_T *cctx); int generate_LISTAPPEND(cctx_T *cctx);
int generate_BLOBAPPEND(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 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 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); int generate_PCALL(cctx_T *cctx, int argcount, char_u *name, type_T *type, int at_top);

View File

@ -921,6 +921,27 @@ def Test_class_new_with_object_member()
Check() Check()
END END
v9.CheckSourceFailure(lines, 'E1013:') 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 enddef
def Test_class_object_member_inits() def Test_class_object_member_inits()

View File

@ -65,6 +65,7 @@ one_function_arg(
garray_T *newargs, garray_T *newargs,
garray_T *argtypes, garray_T *argtypes,
int types_optional, int types_optional,
garray_T *arg_objm,
evalarg_T *evalarg, evalarg_T *evalarg,
exarg_T *eap, exarg_T *eap,
int is_vararg, int is_vararg,
@ -136,7 +137,8 @@ one_function_arg(
} }
// get any type from "arg: type" // 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; char_u *type = NULL;
@ -172,6 +174,7 @@ one_function_arg(
type = vim_strsave((char_u *) type = vim_strsave((char_u *)
(is_vararg ? "list<any>" : "any")); (is_vararg ? "list<any>" : "any"));
((char_u **)argtypes->ga_data)[argtypes->ga_len++] = type; ((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 *newargs,
garray_T *argtypes, // NULL unless using :def garray_T *argtypes, // NULL unless using :def
int types_optional, // types optional if "argtypes" is not NULL int types_optional, // types optional if "argtypes" is not NULL
garray_T *arg_objm, // NULL unless using :def
evalarg_T *evalarg, // context or NULL evalarg_T *evalarg, // context or NULL
int *varargs, int *varargs,
garray_T *default_args, garray_T *default_args,
@ -241,6 +245,8 @@ get_function_args(
ga_init2(newargs, sizeof(char_u *), 3); ga_init2(newargs, sizeof(char_u *), 3);
if (argtypes != NULL) if (argtypes != NULL)
ga_init2(argtypes, sizeof(char_u *), 3); ga_init2(argtypes, sizeof(char_u *), 3);
if (arg_objm != NULL)
ga_init2(arg_objm, sizeof(int8_T), 3);
if (!skip && default_args != NULL) if (!skip && default_args != NULL)
ga_init2(default_args, sizeof(char_u *), 3); ga_init2(default_args, sizeof(char_u *), 3);
@ -295,7 +301,7 @@ get_function_args(
arg = p; arg = p;
p = one_function_arg(p, newargs, argtypes, types_optional, p = one_function_arg(p, newargs, argtypes, types_optional,
evalarg, eap, TRUE, skip); arg_objm, evalarg, eap, TRUE, skip);
if (p == arg) if (p == arg)
break; break;
if (*skipwhite(p) == '=') if (*skipwhite(p) == '=')
@ -350,11 +356,13 @@ get_function_args(
vim_strnsave(arg, argend - arg); vim_strnsave(arg, argend - arg);
newargs->ga_len++; 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 // TODO: use the actual type
((char_u **)argtypes->ga_data)[argtypes->ga_len++] = ((char_u **)argtypes->ga_data)[argtypes->ga_len++] =
vim_strsave((char_u *)"any"); 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. // Add a line to the function body for the assignment.
if (ga_grow(newlines, 1) == OK) if (ga_grow(newlines, 1) == OK)
@ -391,7 +399,7 @@ get_function_args(
arg = p; arg = p;
p = one_function_arg(p, newargs, argtypes, types_optional, p = one_function_arg(p, newargs, argtypes, types_optional,
evalarg, eap, FALSE, skip); arg_objm, evalarg, eap, FALSE, skip);
if (p == arg) if (p == arg)
break; break;
@ -490,7 +498,13 @@ err_ret:
* Return OK or FAIL. * Return OK or FAIL.
*/ */
static int 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; 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 // will get the type from the default value
type = &t_unknown; type = &t_unknown;
else 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) if (type == NULL)
return FAIL; return FAIL;
fp->uf_arg_types[i] = type; fp->uf_arg_types[i] = type;
@ -1395,7 +1426,7 @@ lambda_function_body(
SOURCING_LNUM = sourcing_lnum_top; SOURCING_LNUM = sourcing_lnum_top;
// parse argument types // 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; SOURCING_LNUM = lnum_save;
goto erret; goto erret;
@ -1464,6 +1495,7 @@ get_lambda_tv(
garray_T *pnewargs; garray_T *pnewargs;
garray_T argtypes; garray_T argtypes;
garray_T default_args; garray_T default_args;
garray_T arg_objm;
ufunc_T *fp = NULL; ufunc_T *fp = NULL;
partial_T *pt = NULL; partial_T *pt = NULL;
int varargs; int varargs;
@ -1490,12 +1522,16 @@ get_lambda_tv(
// be found after the arguments. // be found after the arguments.
s = *arg + 1; s = *arg + 1;
ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL, ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
types_optional ? &argtypes : NULL, types_optional, evalarg, types_optional ? &argtypes : NULL, types_optional,
NULL, &default_args, TRUE, NULL, FALSE, NULL, NULL); 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 (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL)
{ {
if (types_optional) if (types_optional)
{
ga_clear_strings(&argtypes); ga_clear_strings(&argtypes);
ga_clear(&arg_objm);
}
return called_emsg == called_emsg_start ? NOTDONE : FAIL; return called_emsg == called_emsg_start ? NOTDONE : FAIL;
} }
@ -1506,7 +1542,8 @@ get_lambda_tv(
pnewargs = NULL; pnewargs = NULL;
*arg += 1; *arg += 1;
ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs, 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, &varargs, &default_args,
FALSE, NULL, FALSE, NULL, NULL); FALSE, NULL, FALSE, NULL, NULL);
if (ret == FAIL if (ret == FAIL
@ -1514,7 +1551,10 @@ get_lambda_tv(
equal_arrow || vim9script ? &white_error : NULL)) == NULL) equal_arrow || vim9script ? &white_error : NULL)) == NULL)
{ {
if (types_optional) if (types_optional)
{
ga_clear_strings(&argtypes); ga_clear_strings(&argtypes);
ga_clear(&arg_objm);
}
ga_clear_strings(&newargs); ga_clear_strings(&newargs);
return white_error ? FAIL : NOTDONE; return white_error ? FAIL : NOTDONE;
} }
@ -1631,7 +1671,7 @@ get_lambda_tv(
if (types_optional) if (types_optional)
{ {
if (parse_argument_types(fp, &argtypes, if (parse_argument_types(fp, &argtypes,
vim9script && varargs) == FAIL) vim9script && varargs, NULL, NULL, 0) == FAIL)
goto errret; goto errret;
if (ret_type != NULL) if (ret_type != NULL)
{ {
@ -4591,7 +4631,9 @@ define_function(
exarg_T *eap, exarg_T *eap,
char_u *name_arg, char_u *name_arg,
garray_T *lines_to_free, garray_T *lines_to_free,
int class_flags) int class_flags,
ocmember_T *obj_members,
int obj_member_count)
{ {
int j; int j;
int c; int c;
@ -4604,6 +4646,7 @@ define_function(
char_u *line_arg = NULL; char_u *line_arg = NULL;
garray_T newargs; garray_T newargs;
garray_T argtypes; garray_T argtypes;
garray_T arg_objm;
garray_T default_args; garray_T default_args;
garray_T newlines; garray_T newlines;
int varargs = FALSE; int varargs = FALSE;
@ -4662,6 +4705,7 @@ define_function(
ga_init(&newargs); ga_init(&newargs);
ga_init(&argtypes); ga_init(&argtypes);
ga_init(&arg_objm);
ga_init(&default_args); ga_init(&default_args);
/* /*
@ -4909,6 +4953,7 @@ define_function(
++p; ++p;
if (get_function_args(&p, ')', &newargs, if (get_function_args(&p, ')', &newargs,
eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
eap->cmdidx == CMD_def ? &arg_objm : NULL,
NULL, &varargs, &default_args, eap->skip, NULL, &varargs, &default_args, eap->skip,
eap, class_flags, &newlines, lines_to_free) == FAIL) eap, class_flags, &newlines, lines_to_free) == FAIL)
goto errret_2; goto errret_2;
@ -5252,7 +5297,8 @@ define_function(
// The function may use script variables from the context. // The function may use script variables from the context.
function_using_block_scopes(fp, cstack); 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; SOURCING_LNUM = lnum_save;
free_fp = fp_allocated; free_fp = fp_allocated;
@ -5352,6 +5398,7 @@ errret_2:
VIM_CLEAR(fp); VIM_CLEAR(fp);
ret_free: ret_free:
ga_clear_strings(&argtypes); ga_clear_strings(&argtypes);
ga_clear(&arg_objm);
vim_free(fudi.fd_newkey); vim_free(fudi.fd_newkey);
if (name != name_arg) if (name != name_arg)
vim_free(name); vim_free(name);
@ -5370,7 +5417,7 @@ ex_function(exarg_T *eap)
garray_T lines_to_free; garray_T lines_to_free;
ga_init2(&lines_to_free, sizeof(char_u *), 50); 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); ga_clear_strings(&lines_to_free);
} }

View File

@ -699,6 +699,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1928,
/**/ /**/
1927, 1927,
/**/ /**/

View File

@ -1200,7 +1200,8 @@ add_default_constructor(
garray_T lines_to_free; garray_T lines_to_free;
ga_init2(&lines_to_free, sizeof(char_u *), 50); 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); ga_clear_strings(&lines_to_free);
vim_free(fga.ga_data); vim_free(fga.ga_data);
@ -1679,7 +1680,7 @@ early_ret:
else else
class_flags = CF_INTERFACE; class_flags = CF_INTERFACE;
ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, 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); ga_clear_strings(&lines_to_free);
if (uf != NULL) if (uf != NULL)

View File

@ -1101,7 +1101,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
int save_KeyTyped = KeyTyped; int save_KeyTyped = KeyTyped;
KeyTyped = FALSE; 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; KeyTyped = save_KeyTyped;

View File

@ -4470,7 +4470,7 @@ exec_instructions(ectx_T *ectx)
ea.cmd = ea.arg = iptr->isn_arg.string; ea.cmd = ea.arg = iptr->isn_arg.string;
ga_init2(&lines_to_free, sizeof(char_u *), 50); ga_init2(&lines_to_free, sizeof(char_u *), 50);
SOURCING_LNUM = iptr->isn_lnum; 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); ga_clear_strings(&lines_to_free);
} }
break; break;

View File

@ -395,8 +395,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
if (type->tt_type == VAR_OBJECT if (type->tt_type == VAR_OBJECT
&& (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED))) && (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
return generate_CALL(cctx, ufunc, cl, fi, type, argcount); return generate_CALL(cctx, ufunc, cl, fi, argcount);
return generate_CALL(cctx, ufunc, NULL, 0, type, argcount); return generate_CALL(cctx, ufunc, NULL, 0, argcount);
} }
if (type->tt_type == VAR_OBJECT) if (type->tt_type == VAR_OBJECT)
@ -977,7 +977,6 @@ compile_call(
int has_g_namespace; int has_g_namespace;
ca_special_T special_fn; ca_special_T special_fn;
imported_T *import; imported_T *import;
type_T *type;
if (varlen >= sizeof(namebuf)) if (varlen >= sizeof(namebuf))
{ {
@ -1061,7 +1060,6 @@ compile_call(
if (compile_arguments(arg, cctx, &argcount, special_fn) == FAIL) if (compile_arguments(arg, cctx, &argcount, special_fn) == FAIL)
goto theend; goto theend;
type = get_decl_type_on_stack(cctx, 1);
is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL; is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL;
if (ASCII_ISLOWER(*name) && name[1] != ':' && !is_autoload) if (ASCII_ISLOWER(*name) && name[1] != ':' && !is_autoload)
{ {
@ -1079,6 +1077,8 @@ compile_call(
if (STRCMP(name, "add") == 0 && argcount == 2) 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 // add() can be compiled to instructions if we know the type
if (type->tt_type == VAR_LIST) if (type->tt_type == VAR_LIST)
{ {
@ -1128,7 +1128,7 @@ compile_call(
{ {
if (!func_is_global(ufunc)) if (!func_is_global(ufunc))
{ {
res = generate_CALL(cctx, ufunc, NULL, 0, type, argcount); res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
goto theend; goto theend;
} }
if (!has_g_namespace if (!has_g_namespace
@ -1147,7 +1147,7 @@ compile_call(
if (cctx->ctx_ufunc->uf_defclass == cl) if (cctx->ctx_ufunc->uf_defclass == cl)
{ {
res = generate_CALL(cctx, cl->class_class_functions[mi], NULL, res = generate_CALL(cctx, cl->class_class_functions[mi], NULL,
0, type, argcount); 0, argcount);
} }
else else
{ {
@ -1175,7 +1175,7 @@ compile_call(
// If we can find a global function by name generate the right call. // If we can find a global function by name generate the right call.
if (ufunc != NULL) if (ufunc != NULL)
{ {
res = generate_CALL(cctx, ufunc, NULL, 0, type, argcount); res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
goto theend; goto theend;
} }

View File

@ -1784,7 +1784,6 @@ generate_CALL(
ufunc_T *ufunc, ufunc_T *ufunc,
class_T *cl, class_T *cl,
int mi, int mi,
type_T *mtype, // method type
int pushed_argcount) int pushed_argcount)
{ {
isn_T *isn; isn_T *isn;
@ -1810,8 +1809,6 @@ generate_CALL(
{ {
int i; int i;
compiletype_T compile_type; 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) for (i = 0; i < argcount; ++i)
{ {
@ -1830,18 +1827,6 @@ generate_CALL(
if (ufunc->uf_arg_types == NULL) if (ufunc->uf_arg_types == NULL)
continue; continue;
expected = ufunc->uf_arg_types[i]; 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 else if (ufunc->uf_va_type == NULL
|| ufunc->uf_va_type == &t_list_any) || ufunc->uf_va_type == &t_list_any)