To: vim_dev@googlegroups.com Subject: Patch 9.0.1178 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1178 Problem: A child class cannot override functions from a base class. Solution: Allow overriding and implement "super". Files: src/vim9class.c, src/structs.h, src/errors.h, src/vim9expr.c, src/globals.h, src/vim9compile.c, src/testdir/test_vim9_class.vim *** ../vim-9.0.1177/src/vim9class.c 2023-01-08 19:54:06.948281440 +0000 --- src/vim9class.c 2023-01-11 15:47:43.097274255 +0000 *************** *** 487,495 **** if (uf != NULL) { ! int is_new = STRNCMP(uf->uf_name, "new", 3) == 0; garray_T *fgap = has_static || is_new ? &classfunctions : &objmethods; if (ga_grow(fgap, 1) == OK) { if (is_new) --- 487,507 ---- if (uf != NULL) { ! char_u *name = uf->uf_name; ! int is_new = STRNCMP(name, "new", 3) == 0; garray_T *fgap = has_static || is_new ? &classfunctions : &objmethods; + // Check the name isn't used already. + for (int i = 0; i < fgap->ga_len; ++i) + { + char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name; + if (STRCMP(name, n) == 0) + { + semsg(_(e_duplicate_function_str), name); + break; + } + } + if (ga_grow(fgap, 1) == OK) { if (is_new) *************** *** 793,799 **** if (nf != NULL && ga_grow(&classfunctions, 1) == OK) { ! ((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len] = nf; ++classfunctions.ga_len; nf->uf_flags |= FC_NEW; --- 805,812 ---- if (nf != NULL && ga_grow(&classfunctions, 1) == OK) { ! ((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len] ! = nf; ++classfunctions.ga_len; nf->uf_flags |= FC_NEW; *************** *** 808,813 **** --- 821,827 ---- } } + // Move all the functions into the created class. // loop 1: class functions, loop 2: object methods for (int loop = 1; loop <= 2; ++loop) { *************** *** 834,859 **** if (*fup == NULL) goto cleanup; int skipped = 0; for (int i = 0; i < parent_count; ++i) { // Copy functions from the parent. Can't use the same // function, because "uf_class" is different and compilation // will have a different result. // Skip "new" functions. TODO: not all of them. if (loop == 1 && STRNCMP( extends_cl->class_class_functions[i]->uf_name, "new", 3) == 0) ++skipped; else ! *fup[i - skipped] = copy_function((loop == 1 ? extends_cl->class_class_functions ! : extends_cl->class_obj_methods)[i]); } - mch_memmove(*fup + parent_count - skipped, gap->ga_data, - sizeof(ufunc_T *) * gap->ga_len); - vim_free(gap->ga_data); *fcount -= skipped; // Set the class pointer on all the functions and object methods. --- 848,899 ---- if (*fup == NULL) goto cleanup; + mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len); + vim_free(gap->ga_data); + if (loop == 1) + cl->class_class_function_count_child = gap->ga_len; + else + cl->class_obj_method_count_child = gap->ga_len; + int skipped = 0; for (int i = 0; i < parent_count; ++i) { // Copy functions from the parent. Can't use the same // function, because "uf_class" is different and compilation // will have a different result. + // Put them after the functions in the current class, object + // methods may be overruled, then "super.Method()" is used to + // find a method from the parent. // Skip "new" functions. TODO: not all of them. if (loop == 1 && STRNCMP( extends_cl->class_class_functions[i]->uf_name, "new", 3) == 0) ++skipped; else ! { ! ufunc_T *pf = (loop == 1 ? extends_cl->class_class_functions ! : extends_cl->class_obj_methods)[i]; ! (*fup)[gap->ga_len + i - skipped] = copy_function(pf); ! ! // If the child class overrides a function from the parent ! // the signature must be equal. ! char_u *pname = pf->uf_name; ! for (int ci = 0; ci < gap->ga_len; ++ci) ! { ! ufunc_T *cf = (*fup)[ci]; ! char_u *cname = cf->uf_name; ! if (STRCMP(pname, cname) == 0) ! { ! where_T where = WHERE_INIT; ! where.wt_func_name = (char *)pname; ! (void)check_type(pf->uf_func_type, cf->uf_func_type, ! TRUE, where); ! } ! } ! } } *fcount -= skipped; // Set the class pointer on all the functions and object methods. *** ../vim-9.0.1177/src/structs.h 2023-01-08 19:54:06.948281440 +0000 --- src/structs.h 2023-01-11 15:39:25.683100504 +0000 *************** *** 1465,1470 **** --- 1465,1471 ---- #define TTFLAG_NUMBER_OK 0x04 // tt_type is VAR_FLOAT, VAR_NUMBER is OK #define TTFLAG_STATIC 0x08 // one of the static types, e.g. t_any #define TTFLAG_CONST 0x10 // cannot be changed + #define TTFLAG_SUPER 0x20 // object from "super". typedef enum { ACCESS_PRIVATE, // read/write only inside th class *************** *** 1506,1512 **** typval_T *class_members_tv; // allocated array of class member vals // class functions: "static def SomeMethod()" ! int class_class_function_count; ufunc_T **class_class_functions; // allocated // object members: "this.varname" --- 1507,1514 ---- typval_T *class_members_tv; // allocated array of class member vals // class functions: "static def SomeMethod()" ! int class_class_function_count; // total count ! int class_class_function_count_child; // count without "extends" ufunc_T **class_class_functions; // allocated // object members: "this.varname" *************** *** 1514,1520 **** ocmember_T *class_obj_members; // allocated // object methods: "def SomeMethod()" ! int class_obj_method_count; ufunc_T **class_obj_methods; // allocated garray_T class_type_list; // used for type pointers --- 1516,1523 ---- ocmember_T *class_obj_members; // allocated // object methods: "def SomeMethod()" ! int class_obj_method_count; // total count ! int class_obj_method_count_child; // count without "extends" ufunc_T **class_obj_methods; // allocated garray_T class_type_list; // used for type pointers *** ../vim-9.0.1177/src/errors.h 2023-01-08 19:54:06.948281440 +0000 --- src/errors.h 2023-01-11 15:04:59.783757216 +0000 *************** *** 3432,3435 **** --- 3432,3441 ---- INIT(= N_("E1353: Class name not found: %s")); EXTERN char e_cannot_extend_str[] INIT(= N_("E1354: Cannot extend %s")); + EXTERN char e_duplicate_function_str[] + INIT(= N_("E1355: Duplicate function: %s")); + EXTERN char e_super_must_be_followed_by_dot[] + INIT(= N_("E1356: \"super\" must be followed by a dot")); + EXTERN char e_using_super_not_in_class_function[] + INIT(= N_("E1357: Using \"super\" not in a class function")); #endif *** ../vim-9.0.1177/src/vim9expr.c 2023-01-09 15:10:36.241547554 +0000 --- src/vim9expr.c 2023-01-11 15:40:50.726987703 +0000 *************** *** 263,269 **** return FAIL; } ! if (type->tt_type == VAR_CLASS) { garray_T *instr = &cctx->ctx_instr; if (instr->ga_len > 0) --- 263,283 ---- return FAIL; } ! class_T *cl = (class_T *)type->tt_member; ! int is_super = type->tt_flags & TTFLAG_SUPER; ! if (type == &t_super) ! { ! if (cctx->ctx_ufunc == NULL || cctx->ctx_ufunc->uf_class == NULL) ! emsg(_(e_using_super_not_in_class_function)); ! else ! { ! is_super = TRUE; ! cl = cctx->ctx_ufunc->uf_class; ! // Remove &t_super from the stack. ! --cctx->ctx_type_stack.ga_len; ! } ! } ! else if (type->tt_type == VAR_CLASS) { garray_T *instr = &cctx->ctx_instr; if (instr->ga_len > 0) *************** *** 286,311 **** return FAIL; size_t len = name_end - name; - class_T *cl = (class_T *)type->tt_member; if (*name_end == '(') { int function_count; ufunc_T **functions; if (type->tt_type == VAR_CLASS) { function_count = cl->class_class_function_count; functions = cl->class_class_functions; } else { // type->tt_type == VAR_OBJECT: method call function_count = cl->class_obj_method_count; functions = cl->class_obj_methods; } ufunc_T *ufunc = NULL; ! for (int i = 0; i < function_count; ++i) { ufunc_T *fp = functions[i]; // Use a separate pointer to avoid that ASAN complains about --- 300,327 ---- return FAIL; size_t len = name_end - name; if (*name_end == '(') { int function_count; + int child_count; ufunc_T **functions; if (type->tt_type == VAR_CLASS) { function_count = cl->class_class_function_count; + child_count = cl->class_class_function_count_child; functions = cl->class_class_functions; } else { // type->tt_type == VAR_OBJECT: method call function_count = cl->class_obj_method_count; + child_count = cl->class_obj_method_count_child; functions = cl->class_obj_methods; } ufunc_T *ufunc = NULL; ! for (int i = is_super ? child_count : 0; i < function_count; ++i) { ufunc_T *fp = functions[i]; // Use a separate pointer to avoid that ASAN complains about *************** *** 643,649 **** if (name == NULL) return FAIL; ! if (vim_strchr(name, AUTOLOAD_CHAR) != NULL) { script_autoload(name, FALSE); res = generate_LOAD(cctx, ISN_LOADAUTO, 0, name, &t_any); --- 659,675 ---- if (name == NULL) return FAIL; ! if (STRCMP(name, "super") == 0 ! && cctx->ctx_ufunc != NULL ! && (cctx->ctx_ufunc->uf_flags & (FC_OBJECT|FC_NEW)) == 0) ! { ! // super.SomeFunc() in a class function: push &t_super type, this ! // is recognized in compile_subscript(). ! res = push_type_stack(cctx, &t_super); ! if (*end != '.') ! emsg(_(e_super_must_be_followed_by_dot)); ! } ! else if (vim_strchr(name, AUTOLOAD_CHAR) != NULL) { script_autoload(name, FALSE); res = generate_LOAD(cctx, ISN_LOADAUTO, 0, name, &t_any); *** ../vim-9.0.1177/src/globals.h 2022-12-26 14:37:40.428187675 +0000 --- src/globals.h 2023-01-11 14:51:56.836343057 +0000 *************** *** 527,533 **** #define t_dict_string (static_types[76]) #define t_const_dict_string (static_types[77]) ! EXTERN type_T static_types[78] #ifdef DO_INIT = { // 0: t_unknown --- 527,536 ---- #define t_dict_string (static_types[76]) #define t_const_dict_string (static_types[77]) ! #define t_super (static_types[78]) ! #define t_const_super (static_types[79]) ! ! EXTERN type_T static_types[80] #ifdef DO_INIT = { // 0: t_unknown *************** *** 685,690 **** --- 688,697 ---- // 76: t_dict_string {VAR_DICT, 0, 0, TTFLAG_STATIC, &t_string, NULL}, {VAR_DICT, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_string, NULL}, + + // 78: t_super (VAR_CLASS with tt_member set to &t_bool + {VAR_CLASS, 0, 0, TTFLAG_STATIC, &t_bool, NULL}, + {VAR_CLASS, 0, 0, TTFLAG_STATIC|TTFLAG_CONST, &t_bool, NULL}, } #endif ; *** ../vim-9.0.1177/src/vim9compile.c 2023-01-05 19:59:14.003418087 +0000 --- src/vim9compile.c 2023-01-11 15:38:32.223172211 +0000 *************** *** 43,58 **** if (len == 0) return FAIL; ! if (len == 4 && STRNCMP(name, "this", 4) == 0 && cctx->ctx_ufunc != NULL && (cctx->ctx_ufunc->uf_flags & (FC_OBJECT|FC_NEW))) { if (lvar != NULL) { CLEAR_POINTER(lvar); ! lvar->lv_name = (char_u *)"this"; if (cctx->ctx_ufunc->uf_class != NULL) lvar->lv_type = &cctx->ctx_ufunc->uf_class->class_object_type; } return OK; } --- 43,73 ---- if (len == 0) return FAIL; ! if (((len == 4 && STRNCMP(name, "this", 4) == 0) ! || (len == 5 && STRNCMP(name, "super", 5) == 0)) && cctx->ctx_ufunc != NULL && (cctx->ctx_ufunc->uf_flags & (FC_OBJECT|FC_NEW))) { + int is_super = *name == 's'; if (lvar != NULL) { CLEAR_POINTER(lvar); ! lvar->lv_name = (char_u *)(is_super ? "super" : "this"); if (cctx->ctx_ufunc->uf_class != NULL) + { lvar->lv_type = &cctx->ctx_ufunc->uf_class->class_object_type; + if (is_super) + { + type_T *type = get_type_ptr(cctx->ctx_type_list); + + if (type != NULL) + { + *type = *lvar->lv_type; + lvar->lv_type = type; + type->tt_flags |= TTFLAG_SUPER; + } + } + } } return OK; } *** ../vim-9.0.1177/src/testdir/test_vim9_class.vim 2023-01-08 19:54:06.952281443 +0000 --- src/testdir/test_vim9_class.vim 2023-01-11 15:45:14.604376547 +0000 *************** *** 817,822 **** --- 817,843 ---- endclass END v9.CheckScriptFailure(lines, 'E1354: Cannot extend SomeVar') + + lines =<< trim END + vim9script + class Base + this.name: string + def ToString(): string + return this.name + enddef + endclass + + class Child extends Base + this.age: number + def ToString(): string + return super.ToString() .. ': ' .. this.age + enddef + endclass + + var o = Child.new('John', 42) + assert_equal('John: 42', o.ToString()) + END + v9.CheckScriptSuccess(lines) enddef *** ../vim-9.0.1177/src/version.c 2023-01-11 12:49:19.533434295 +0000 --- src/version.c 2023-01-11 13:27:12.860837154 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1178, /**/ -- Engineers are always delighted to share wisdom, even in areas in which they have no experience whatsoever. Their logic provides them with inherent insight into any field of expertise. This can be a problem when dealing with the illogical people who believe that knowledge can only be derived through experience. (Scott Adams - The Dilbert principle) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///