Patch 9.0.1254
Problem:    Calling a method on an interface does not work.
Solution:   At runtime figure out what method to call. (closes #11901)
Files:      src/vim9expr.c, src/vim9instr.c, src/proto/vim9instr.pro,
            src/vim9.h, src/vim9execute.c, src/vim9class.c, src/structs.h,
            src/proto/vim9class.pro, src/testdir/test_vim9_class.vim


*** ../vim-9.0.1253/src/vim9expr.c      2023-01-27 20:13:58.432454836 +0000
--- src/vim9expr.c      2023-01-28 14:24:19.423670412 +0000
***************
*** 321,329 ****
        }
  
        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
            // uf_name[] only being 4 characters.
            char_u *ufname = (char_u *)fp->uf_name;
--- 321,330 ----
        }
  
        ufunc_T *ufunc = NULL;
!       int fi;
!       for (fi = is_super ? child_count : 0; fi < function_count; ++fi)
        {
!           ufunc_T *fp = functions[fi];
            // Use a separate pointer to avoid that ASAN complains about
            // uf_name[] only being 4 characters.
            char_u *ufname = (char_u *)fp->uf_name;
***************
*** 347,353 ****
        int argcount = 0;
        if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
            return FAIL;
!       return generate_CALL(cctx, ufunc, argcount);
      }
  
      if (type->tt_type == VAR_OBJECT)
--- 348,358 ----
        int argcount = 0;
        if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
            return FAIL;
! 
!       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);
      }
  
      if (type->tt_type == VAR_OBJECT)
***************
*** 364,370 ****
                }
  
                *arg = name_end;
!               if (cl->class_flags & CLASS_INTERFACE)
                    return generate_GET_ITF_MEMBER(cctx, cl, i, m->ocm_type);
                return generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type);
            }
--- 369,375 ----
                }
  
                *arg = name_end;
!               if (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED))
                    return generate_GET_ITF_MEMBER(cctx, cl, i, m->ocm_type);
                return generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type);
            }
***************
*** 1063,1069 ****
        {
            if (!func_is_global(ufunc))
            {
!               res = generate_CALL(cctx, ufunc, argcount);
                goto theend;
            }
            if (!has_g_namespace
--- 1068,1074 ----
        {
            if (!func_is_global(ufunc))
            {
!               res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
                goto theend;
            }
            if (!has_g_namespace
***************
*** 1092,1098 ****
      // If we can find a global function by name generate the right call.
      if (ufunc != NULL)
      {
!       res = generate_CALL(cctx, ufunc, argcount);
        goto theend;
      }
  
--- 1097,1103 ----
      // If we can find a global function by name generate the right call.
      if (ufunc != NULL)
      {
!       res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
        goto theend;
      }
  
*** ../vim-9.0.1253/src/vim9instr.c     2023-01-27 20:13:58.436454834 +0000
--- src/vim9instr.c     2023-01-28 12:00:01.225053225 +0000
***************
*** 1709,1719 ****
  }
  
  /*
!  * Generate an ISN_DCALL or ISN_UCALL instruction.
   * Return FAIL if the number of arguments is wrong.
   */
      int
! generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
  {
      isn_T     *isn;
      int               regular_args = ufunc->uf_args.ga_len;
--- 1709,1726 ----
  }
  
  /*
!  * Generate an ISN_DCALL, ISN_UCALL or ISN_METHODCALL instruction.
!  * When calling a method on an object, of which we know the interface only,
!  * then "cl" is the interface and "mi" the method index on the interface.
   * Return FAIL if the number of arguments is wrong.
   */
      int
! generate_CALL(
!       cctx_T      *cctx,
!       ufunc_T     *ufunc,
!       class_T     *cl,
!       int         mi,
!       int         pushed_argcount)
  {
      isn_T     *isn;
      int               regular_args = ufunc->uf_args.ga_len;
***************
*** 1783,1793 ****
        return FAIL;
      }
  
!     if ((isn = generate_instr(cctx,
!                   ufunc->uf_def_status != UF_NOT_COMPILED ? ISN_DCALL
!                                                        : ISN_UCALL)) == NULL)
        return FAIL;
!     if (isn->isn_type == ISN_DCALL)
      {
        isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
        isn->isn_arg.dfunc.cdf_argcount = argcount;
--- 1790,1810 ----
        return FAIL;
      }
  
!     if ((isn = generate_instr(cctx, cl != NULL ? ISN_METHODCALL
!                         : ufunc->uf_def_status != UF_NOT_COMPILED
!                                            ? ISN_DCALL : ISN_UCALL)) == NULL)
        return FAIL;
!     if (isn->isn_type == ISN_METHODCALL)
!     {
!       isn->isn_arg.mfunc = ALLOC_ONE(cmfunc_T);
!       if (isn->isn_arg.mfunc == NULL)
!           return FAIL;
!       isn->isn_arg.mfunc->cmf_itf = cl;
!       ++cl->class_refcount;
!       isn->isn_arg.mfunc->cmf_idx = mi;
!       isn->isn_arg.mfunc->cmf_argcount = argcount;
!     }
!     else if (isn->isn_type == ISN_DCALL)
      {
        isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
        isn->isn_arg.dfunc.cdf_argcount = argcount;
***************
*** 2483,2488 ****
--- 2500,2513 ----
            }
            break;
  
+       case ISN_METHODCALL:
+           {
+               cmfunc_T  *mfunc = isn->isn_arg.mfunc;
+               class_unref(mfunc->cmf_itf);
+               vim_free(mfunc);
+           }
+           break;
+ 
        case ISN_NEWFUNC:
            {
                newfuncarg_T *arg = isn->isn_arg.newfunc.nf_arg;
*** ../vim-9.0.1253/src/proto/vim9instr.pro     2023-01-27 20:13:58.436454834 
+0000
--- src/proto/vim9instr.pro     2023-01-28 12:00:12.625044316 +0000
***************
*** 57,63 ****
  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, 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);
--- 57,63 ----
  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_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);
*** ../vim-9.0.1253/src/vim9.h  2023-01-27 20:13:58.436454834 +0000
--- src/vim9.h  2023-01-28 11:55:00.541324743 +0000
***************
*** 112,117 ****
--- 112,118 ----
      // function call
      ISN_BCALL,            // call builtin function isn_arg.bfunc
      ISN_DCALL,            // call def function isn_arg.dfunc
+     ISN_METHODCALL, // call method on interface, uses isn_arg.mfunc
      ISN_UCALL,            // call user function or funcref/partial 
isn_arg.ufunc
      ISN_PCALL,            // call partial, use isn_arg.pfunc
      ISN_PCALL_END,  // cleanup after ISN_PCALL with cpf_top set
***************
*** 234,239 ****
--- 235,247 ----
      int           cdf_argcount;   // number of arguments on top of stack
  } cdfunc_T;
  
+ // arguments to ISN_METHODCALL
+ typedef struct {
+     class_T *cmf_itf;     // interface used
+     int           cmf_idx;        // index in "def_functions" for ISN_DCALL
+     int           cmf_argcount;   // number of arguments on top of stack
+ } cmfunc_T;
+ 
  // arguments to ISN_PCALL
  typedef struct {
      int           cpf_top;        // when TRUE partial is above the arguments
***************
*** 517,522 ****
--- 525,531 ----
        trycont_T           trycont;
        cbfunc_T            bfunc;
        cdfunc_T            dfunc;
+       cmfunc_T            *mfunc;
        cpfunc_T            pfunc;
        cufunc_T            ufunc;
        echo_T              echo;
*** ../vim-9.0.1253/src/vim9execute.c   2023-01-27 20:13:58.436454834 +0000
--- src/vim9execute.c   2023-01-28 13:50:40.552626610 +0000
***************
*** 2262,2268 ****
            class_T         *itf = iptr->isn_arg.storeindex.si_class;
            if (itf != NULL)
                // convert interface member index to class member index
!               idx = object_index_from_itf_index(itf, idx, obj->obj_class);
  
            clear_tv(&otv[idx]);
            otv[idx] = *tv;
--- 2262,2269 ----
            class_T         *itf = iptr->isn_arg.storeindex.si_class;
            if (itf != NULL)
                // convert interface member index to class member index
!               idx = object_index_from_itf_index(itf, FALSE,
!                                                         idx, obj->obj_class);
  
            clear_tv(&otv[idx]);
            otv[idx] = *tv;
***************
*** 2950,2955 ****
--- 2951,2970 ----
      return OK;
  }
  
+ 
+     static void
+ object_required_error(typval_T *tv)
+ {
+     garray_T type_list;
+     ga_init2(&type_list, sizeof(type_T *), 10);
+     type_T *type = typval2type(tv, get_copyID(), &type_list, TVTT_DO_MEMBER);
+     char *tofree = NULL;
+     char *typename = type_name(type, &tofree);
+     semsg(_(e_object_required_found_str), typename);
+     vim_free(tofree);
+     clear_type_list(&type_list);
+ }
+ 
  /*
   * Execute instructions in execution context "ectx".
   * Return OK or FAIL;
***************
*** 4125,4130 ****
--- 4140,4169 ----
                    goto on_error;
                break;
  
+           // call a method on an interface
+           case ISN_METHODCALL:
+               {
+                   SOURCING_LNUM = iptr->isn_lnum;
+                   tv = STACK_TV_BOT(-1);
+                   if (tv->v_type != VAR_OBJECT)
+                   {
+                       object_required_error(tv);
+                       goto on_error;
+                   }
+                   object_T *obj = tv->vval.v_object;
+                   class_T *cl = obj->obj_class;
+ 
+                   // convert the interface index to the object index
+                   cmfunc_T *mfunc = iptr->isn_arg.mfunc;
+                   int idx = object_index_from_itf_index(mfunc->cmf_itf,
+                                                   TRUE, mfunc->cmf_idx, cl);
+ 
+                   if (call_ufunc(cl->class_obj_methods[idx], NULL,
+                               mfunc->cmf_argcount, ectx, NULL, NULL) == FAIL)
+                       goto on_error;
+               }
+               break;
+ 
            // call a builtin function
            case ISN_BCALL:
                SOURCING_LNUM = iptr->isn_lnum;
***************
*** 5213,5227 ****
                    if (tv->v_type != VAR_OBJECT)
                    {
                        SOURCING_LNUM = iptr->isn_lnum;
!                       garray_T type_list;
!                       ga_init2(&type_list, sizeof(type_T *), 10);
!                       type_T *type = typval2type(tv, get_copyID(),
!                                                  &type_list, TVTT_DO_MEMBER);
!                       char *tofree = NULL;
!                       char *typename = type_name(type, &tofree);
!                       semsg(_(e_object_required_found_str), typename);
!                       vim_free(tofree);
!                       clear_type_list(&type_list);
                        goto on_error;
                    }
  
--- 5252,5258 ----
                    if (tv->v_type != VAR_OBJECT)
                    {
                        SOURCING_LNUM = iptr->isn_lnum;
!                       object_required_error(tv);
                        goto on_error;
                    }
  
***************
*** 5234,5241 ****
                        idx = iptr->isn_arg.classmember.cm_idx;
                        // convert the interface index to the object index
                        idx = object_index_from_itf_index(
!                                             
iptr->isn_arg.classmember.cm_class,
!                                             idx, obj->obj_class);
                    }
  
                    // the members are located right after the object struct
--- 5265,5272 ----
                        idx = iptr->isn_arg.classmember.cm_idx;
                        // convert the interface index to the object index
                        idx = object_index_from_itf_index(
!                                           iptr->isn_arg.classmember.cm_class,
!                                           FALSE, idx, obj->obj_class);
                    }
  
                    // the members are located right after the object struct
***************
*** 6637,6642 ****
--- 6668,6684 ----
                                                         cdfunc->cdf_argcount);
                }
                break;
+           case ISN_METHODCALL:
+               {
+                   cmfunc_T    *mfunc = iptr->isn_arg.mfunc;
+ 
+                   smsg("%s%4d METHODCALL %s.%s(argc %d)", pfx, current,
+                           mfunc->cmf_itf->class_name,
+                           mfunc->cmf_itf->class_obj_methods[
+                                                     mfunc->cmf_idx]->uf_name,
+                           mfunc->cmf_argcount);
+               }
+               break;
            case ISN_UCALL:
                {
                    cufunc_T    *cufunc = &iptr->isn_arg.ufunc;
*** ../vim-9.0.1253/src/vim9class.c     2023-01-27 13:16:14.674850404 +0000
--- src/vim9class.c     2023-01-28 14:51:08.079957103 +0000
***************
*** 201,216 ****
   * "cl" implementing that interface.
   */
      int
! object_index_from_itf_index(class_T *itf, int idx, class_T *cl)
  {
!     if (idx > itf->class_obj_member_count)
      {
        siemsg("index %d out of range for interface %s", idx, itf->class_name);
        return 0;
      }
      itf2class_T *i2c;
      for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
!       if (i2c->i2c_class == cl)
            break;
      if (i2c == NULL)
      {
--- 201,217 ----
   * "cl" implementing that interface.
   */
      int
! object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
  {
!     if (idx > (is_method ? itf->class_obj_method_count
!                                               : itf->class_obj_member_count))
      {
        siemsg("index %d out of range for interface %s", idx, itf->class_name);
        return 0;
      }
      itf2class_T *i2c;
      for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
!       if (i2c->i2c_class == cl && i2c->i2c_is_method == is_method)
            break;
      if (i2c == NULL)
      {
***************
*** 789,795 ****
        if (cl->class_name == NULL)
            goto cleanup;
  
!       cl->class_extends = extends_cl;
  
        // Add class and object members to "cl".
        if (add_members_to_class(&classmembers,
--- 790,800 ----
        if (cl->class_name == NULL)
            goto cleanup;
  
!       if (extends_cl != NULL)
!       {
!           cl->class_extends = extends_cl;
!           extends_cl->class_flags |= CLASS_EXTENDED;
!       }
  
        // Add class and object members to "cl".
        if (add_members_to_class(&classmembers,
***************
*** 820,830 ****
            VIM_CLEAR(ga_impl.ga_data);
            ga_impl.ga_len = 0;
  
            // For each interface add a lookuptable for the member index on the
            // interface to the member index in this class.
!           for (int i = 0; i < cl->class_interface_count; ++i)
!           {
!               class_T *ifcl = intf_classes[i];
                itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
                                 + ifcl->class_obj_member_count * sizeof(int));
                if (if2cl == NULL)
--- 825,850 ----
            VIM_CLEAR(ga_impl.ga_data);
            ga_impl.ga_len = 0;
  
+           cl->class_interfaces_cl = intf_classes;
+           intf_classes = NULL;
+       }
+ 
+       if (cl->class_interface_count > 0 || extends_cl != NULL)
+       {
            // For each interface add a lookuptable for the member index on the
            // interface to the member index in this class.
!           // And a lookuptable for the object method index on the interface
!           // to the object method index in this class.
!           // Also do this for the extended class, if any.
!           for (int i = 0; i <= cl->class_interface_count; ++i)
!           {
!               class_T *ifcl = i < cl->class_interface_count
!                                           ? cl->class_interfaces_cl[i]
!                                           : extends_cl;
!               if (ifcl == NULL)
!                   continue;
! 
!               // Table for members.
                itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
                                 + ifcl->class_obj_member_count * sizeof(int));
                if (if2cl == NULL)
***************
*** 832,853 ****
                if2cl->i2c_next = ifcl->class_itf2class;
                ifcl->class_itf2class = if2cl;
                if2cl->i2c_class = cl;
  
                for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
!                   for (int cl_i = 0; cl_i < cl->class_obj_member_count; 
++cl_i)
                    {
                        if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
!                                    cl->class_obj_members[cl_i].ocm_name) == 0)
                        {
                            int *table = (int *)(if2cl + 1);
                            table[if_i] = cl_i;
                            break;
                        }
                    }
-           }
  
!           cl->class_interfaces_cl = intf_classes;
!           intf_classes = NULL;
        }
  
        if (is_class && cl->class_class_member_count > 0)
--- 852,915 ----
                if2cl->i2c_next = ifcl->class_itf2class;
                ifcl->class_itf2class = if2cl;
                if2cl->i2c_class = cl;
+               if2cl->i2c_is_method = FALSE;
  
                for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
!                   for (int cl_i = 0; cl_i < cl->class_obj_member_count;
!                                                                       ++cl_i)
                    {
                        if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
!                                   cl->class_obj_members[cl_i].ocm_name) == 0)
                        {
                            int *table = (int *)(if2cl + 1);
                            table[if_i] = cl_i;
                            break;
                        }
                    }
  
!               // Table for methods.
!               if2cl = alloc_clear(sizeof(itf2class_T)
!                                + ifcl->class_obj_method_count * sizeof(int));
!               if (if2cl == NULL)
!                   goto cleanup;
!               if2cl->i2c_next = ifcl->class_itf2class;
!               ifcl->class_itf2class = if2cl;
!               if2cl->i2c_class = cl;
!               if2cl->i2c_is_method = TRUE;
! 
!               for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
!               {
!                   int done = FALSE;
!                   for (int cl_i = 0; cl_i < objmethods.ga_len; ++cl_i)
!                   {
!                       if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
!                              ((ufunc_T **)objmethods.ga_data)[cl_i]->uf_name)
!                                                                         == 0)
!                       {
!                           int *table = (int *)(if2cl + 1);
!                           table[if_i] = cl_i;
!                           done = TRUE;
!                           break;
!                       }
!                   }
! 
!                   if (!done && extends_cl != NULL)
!                   {
!                       for (int cl_i = 0;
!                            cl_i < extends_cl->class_obj_member_count; ++cl_i)
!                       {
!                           if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
!                                  extends_cl->class_obj_methods[cl_i]->uf_name)
!                                                                         == 0)
!                           {
!                               int *table = (int *)(if2cl + 1);
!                               table[if_i] = cl_i;
!                               break;
!                           }
!                       }
!                   }
!               }
!           }
        }
  
        if (is_class && cl->class_class_member_count > 0)
*** ../vim-9.0.1253/src/structs.h       2023-01-20 18:49:42.763170966 +0000
--- src/structs.h       2023-01-28 14:21:34.103761488 +0000
***************
*** 1484,1498 ****
      char_u    *ocm_init;   // allocated
  } ocmember_T;
  
! // used for the lookup table of a class member index
  typedef struct itf2class_S itf2class_T;
  struct itf2class_S {
      itf2class_T       *i2c_next;
      class_T   *i2c_class;
      // array with ints follows
  };
  
! #define CLASS_INTERFACE 1
  
  // "class_T": used for v_class of typval of VAR_CLASS
  // Also used for an interface (class_flags has CLASS_INTERFACE).
--- 1484,1500 ----
      char_u    *ocm_init;   // allocated
  } ocmember_T;
  
! // used for the lookup table of a class member index and object method index
  typedef struct itf2class_S itf2class_T;
  struct itf2class_S {
      itf2class_T       *i2c_next;
      class_T   *i2c_class;
+     int               i2c_is_method;      // TRUE for method indexes
      // array with ints follows
  };
  
! #define CLASS_INTERFACE           1
! #define CLASS_EXTENDED            2       // another class extends this one
  
  // "class_T": used for v_class of typval of VAR_CLASS
  // Also used for an interface (class_flags has CLASS_INTERFACE).
*** ../vim-9.0.1253/src/proto/vim9class.pro     2023-01-24 15:07:00.428562536 
+0000
--- src/proto/vim9class.pro     2023-01-28 13:40:21.620461706 +0000
***************
*** 1,5 ****
  /* vim9class.c */
! int object_index_from_itf_index(class_T *itf, int idx, class_T *cl);
  void ex_class(exarg_T *eap);
  type_T *class_member_type(class_T *cl, char_u *name, char_u *name_end, int 
*member_idx);
  void ex_enum(exarg_T *eap);
--- 1,5 ----
  /* vim9class.c */
! int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T 
*cl);
  void ex_class(exarg_T *eap);
  type_T *class_member_type(class_T *cl, char_u *name, char_u *name_end, int 
*member_idx);
  void ex_enum(exarg_T *eap);
*** ../vim-9.0.1253/src/testdir/test_vim9_class.vim     2023-01-27 
20:13:58.436454834 +0000
--- src/testdir/test_vim9_class.vim     2023-01-28 14:02:38.060674983 +0000
***************
*** 1001,1006 ****
--- 1001,1056 ----
    v9.CheckScriptSuccess(lines)
  enddef
  
+ def Test_call_interface_method()
+   var lines =<< trim END
+     vim9script
+     interface Base
+       def Enter(): void
+     endinterface
+ 
+     class Child implements Base
+       def Enter(): void
+         g:result ..= 'child'
+       enddef
+     endclass
+ 
+     def F(obj: Base)
+       obj.Enter()
+     enddef
+ 
+     g:result = ''
+     F(Child.new())
+     assert_equal('child', g:result)
+     unlet g:result
+   END
+   v9.CheckScriptSuccess(lines)
+ 
+   lines =<< trim END
+     vim9script
+     class Base
+       def Enter(): void
+         g:result ..= 'base'
+       enddef
+     endclass
+ 
+     class Child extends Base
+       def Enter(): void
+         g:result ..= 'child'
+       enddef
+     endclass
+ 
+     def F(obj: Base)
+       obj.Enter()
+     enddef
+ 
+     g:result = ''
+     F(Child.new())
+     assert_equal('child', g:result)
+     unlet g:result
+   END
+   v9.CheckScriptSuccess(lines)
+ enddef
+ 
  def Test_class_used_as_type()
    var lines =<< trim END
        vim9script
*** ../vim-9.0.1253/src/version.c       2023-01-28 10:43:47.157153173 +0000
--- src/version.c       2023-01-28 11:42:34.685721166 +0000
***************
*** 697,698 ****
--- 697,700 ----
  {   /* Add new patch number below this line */
+ /**/
+     1254,
  /**/

-- 
"I don’t know how to make a screenshot" - Richard Stallman, July 2002
(when asked to send a screenshot of his desktop for unix.se)

 /// 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/20230128152008.683551C0559%40moolenaar.net.

Raspunde prin e-mail lui