# HG changeset patch # User Christian Brabandt # Date 1692549005 -7200 # Node ID 54c01bb98b8e91128af3b1d97bce1a1270e65a5b # Parent d96544a100e330b70f9a191b3165ad0b71595c4f patch 9.0.1760: vim9 class problem with new() constructor Commit: https://github.com/vim/vim/commit/6ac1544e13d78e0deeca1f357d1d73947180171c Author: Yegappan Lakshmanan Date: Sun Aug 20 18:20:17 2023 +0200 patch 9.0.1760: vim9 class problem with new() constructor Problem: vim9 class problem with new() constructor Solution: Don't allow a return type for the new() class constructor. closes: #12863 closes: #12040 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt --- a/runtime/doc/vim9class.txt +++ b/runtime/doc/vim9class.txt @@ -232,6 +232,9 @@ If the class extends a parent class, the step the members of the parent class are done first. There is no need to call "super()" or "new()" on the parent. +When defining the new() method the return type should not be specified. It +always returns an object of the class. + ============================================================================== 3. class members and functions *Vim9-class-member* diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -3478,6 +3478,8 @@ EXTERN char e_incomplete_type[] #endif EXTERN char e_warning_pointer_block_corrupted[] INIT(= N_("E1364: Warning: Pointer block corrupted")); +EXTERN char e_cannot_use_a_return_type_with_new[] + INIT(= N_("E1365: Cannot use a return type with the \"new\" function")); EXTERN char e_cannot_mix_positional_and_non_positional_str[] INIT(= N_("E1400: Cannot mix positional and non-positional arguments: %s")); EXTERN char e_fmt_arg_nr_unused_str[] @@ -3495,4 +3497,4 @@ EXTERN char e_member_str_type_mismatch_e EXTERN char e_method_str_type_mismatch_expected_str_but_got_str[] INIT(= N_("E1407: Member \"%s\": type mismatch, expected %s but got %s")); -// E1365 - E1399 unused +// E1366 - E1399 unused 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 @@ -1577,6 +1577,16 @@ def Test_class_implements_interface() END v9.CheckScriptFailure(lines, 'E1349:') + # implements should be followed by a white space + lines =<< trim END + vim9script + interface A + endinterface + class B implements A; + endclass + END + v9.CheckScriptFailure(lines, 'E1315:') + lines =<< trim END vim9script @@ -2515,4 +2525,104 @@ def Test_stack_expansion_with_methods() END v9.CheckScriptSuccess(lines) enddef + +" Test the return type of the new() constructor +def Test_new_return_type() + # new() uses the default return type and there is no return statement + var lines =<< trim END + vim9script + + class C + this._bufnr: number + + def new(this._bufnr) + if !bufexists(this._bufnr) + this._bufnr = -1 + endif + enddef + endclass + + var c = C.new(12345) + assert_equal('object', typename(c)) + + var v1: C + v1 = C.new(12345) + assert_equal('object', typename(v1)) + + def F() + var v2: C + v2 = C.new(12345) + assert_equal('object', typename(v2)) + enddef + F() + END + v9.CheckScriptSuccess(lines) + + # new() uses the default return type and an empty 'return' statement + lines =<< trim END + vim9script + + class C + this._bufnr: number + + def new(this._bufnr) + if !bufexists(this._bufnr) + this._bufnr = -1 + return + endif + enddef + endclass + + var c = C.new(12345) + assert_equal('object', typename(c)) + + var v1: C + v1 = C.new(12345) + assert_equal('object', typename(v1)) + + def F() + var v2: C + v2 = C.new(12345) + assert_equal('object', typename(v2)) + enddef + F() + END + v9.CheckScriptSuccess(lines) + + # new() uses "any" return type and returns "this" + lines =<< trim END + vim9script + + class C + this._bufnr: number + + def new(this._bufnr): any + if !bufexists(this._bufnr) + this._bufnr = -1 + return this + endif + enddef + endclass + END + v9.CheckScriptFailure(lines, 'E1365:') + + # new() uses 'Dict' return type and returns a Dict + lines =<< trim END + vim9script + + class C + this._state: dict + + def new(): dict + this._state = {} + return this._state + enddef + endclass + + var c = C.new() + assert_equal('object', typename(c)) + END + v9.CheckScriptFailure(lines, 'E1365:') +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker 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 */ /**/ + 1760, +/**/ 1759, /**/ 1758, diff --git a/src/vim9class.c b/src/vim9class.c --- a/src/vim9class.c +++ b/src/vim9class.c @@ -687,7 +687,7 @@ add_class_members(class_T *cl, exarg_T * } /* - * Add a default constructor to the class "cl". + * Add a default constructor method (new()) to the class "cl". */ static void add_default_constructor( @@ -1150,6 +1150,18 @@ early_ret: func_clear_free(uf, FALSE); break; } + if (is_new) + { + // A return type should not be specified for the new() + // constructor method. + if (uf->uf_ret_type->tt_type != VAR_VOID) + { + emsg(_(e_cannot_use_a_return_type_with_new)); + success = FALSE; + func_clear_free(uf, FALSE); + break; + } + } garray_T *fgap = has_static || is_new ? &classfunctions : &objmethods; // Check the name isn't used already. @@ -1303,15 +1315,22 @@ early_ret: if (is_class && cl->class_class_member_count > 0) add_class_members(cl, eap); - int have_new = FALSE; + int have_new = FALSE; + ufunc_T *class_func = NULL; for (int i = 0; i < classfunctions.ga_len; ++i) - if (STRCMP(((ufunc_T **)classfunctions.ga_data)[i]->uf_name, - "new") == 0) + { + class_func = ((ufunc_T **)classfunctions.ga_data)[i]; + if (STRCMP(class_func->uf_name, "new") == 0) { have_new = TRUE; break; } - if (is_class && !is_abstract && !have_new) + } + + if (have_new) + // The return type of new() is an object of class "cl" + class_func->uf_ret_type->tt_class = cl; + else if (is_class && !is_abstract && !have_new) // No new() method was defined, add the default constructor. add_default_constructor(cl, &classfunctions, &type_list); diff --git a/src/vim9cmds.c b/src/vim9cmds.c --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -2617,8 +2617,16 @@ compile_return(char_u *arg, int check_re return NULL; } - // No argument, return zero. - generate_PUSHNR(cctx, 0); + if (cctx->ctx_ufunc->uf_flags & FC_NEW) + { + // For a class new() constructor, return an object of the class. + generate_instr(cctx, ISN_RETURN_OBJECT); + cctx->ctx_ufunc->uf_ret_type = + &cctx->ctx_ufunc->uf_class->class_object_type; + } + else + // No argument, return zero. + generate_PUSHNR(cctx, 0); } // may need ENDLOOP when inside a :for or :while loop