# HG changeset patch # User Christian Brabandt # Date 1692305103 -7200 # Node ID b3a42579bb3f877d38cbbd99f5b544ed8c886ec2 # Parent 52a61f786e7891356f5d59ec3d38bdbb3dd46061 patch 9.0.1724: vim9class constructor argument type checking bug Commit: https://github.com/vim/vim/commit/2261c89a49ff2115e1ccc9ab9211e9f0d5a37578 Author: h-east 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 Co-authored-by: h-east diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro --- 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); diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim --- 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 diff --git a/src/version.c b/src/version.c --- 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, diff --git a/src/vim9expr.c b/src/vim9expr.c --- 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; } diff --git a/src/vim9instr.c b/src/vim9instr.c --- 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)