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 -- [email protected] -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/20230111155934.692521C07A0%40moolenaar.net.