Patch 8.2.0601
Problem:    Vim9: :unlet is not compiled.
Solution:   Implement :unlet instruction and check for errors.
Files:      src/vim9compile.c, src/proto/vim9compile.pro, src/vim9.h,
            src/vim9execute.c, src/evalvars.c, src/proto/evalvars.pro,
            src/eval.c, src/testdir/test_vim9_script.vim,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.0600/src/vim9compile.c   2020-04-19 14:32:13.556206438 +0200
--- src/vim9compile.c   2020-04-19 16:20:36.753337072 +0200
***************
*** 987,992 ****
--- 987,1009 ----
  }
  
  /*
+  * Generate an ISN_UNLET instruction.
+  */
+     static int
+ generate_UNLET(cctx_T *cctx, char_u *name, int forceit)
+ {
+     isn_T     *isn;
+ 
+     RETURN_OK_IF_SKIP(cctx);
+     if ((isn = generate_instr(cctx, ISN_UNLET)) == NULL)
+       return FAIL;
+     isn->isn_arg.unlet.ul_name = vim_strsave(name);
+     isn->isn_arg.unlet.ul_forceit = forceit;
+ 
+     return OK;
+ }
+ 
+ /*
   * Generate an ISN_LOADS instruction.
   */
      static int
***************
*** 4543,4548 ****
--- 4560,4640 ----
  }
  
  /*
+  * Check if "name" can be "unlet".
+  */
+     int
+ check_vim9_unlet(char_u *name)
+ {
+     if (name[1] != ':' || vim_strchr((char_u *)"gwtb", *name) == NULL)
+     {
+       semsg(_("E1081: Cannot unlet %s"), name);
+       return FAIL;
+     }
+     return OK;
+ }
+ 
+ /*
+  * Callback passed to ex_unletlock().
+  */
+     static int
+ compile_unlet(
+     lval_T  *lvp,
+     char_u  *name_end,
+     exarg_T *eap,
+     int           deep UNUSED,
+     void    *coookie)
+ {
+     cctx_T *cctx = coookie;
+ 
+     if (lvp->ll_tv == NULL)
+     {
+       char_u  *p = lvp->ll_name;
+       int     cc = *name_end;
+       int     ret = OK;
+ 
+       // Normal name.  Only supports g:, w:, t: and b: namespaces.
+       *name_end = NUL;
+       if (check_vim9_unlet(p) == FAIL)
+           ret = FAIL;
+       else
+           ret = generate_UNLET(cctx, p, eap->forceit);
+ 
+       *name_end = cc;
+       return ret;
+     }
+ 
+     // TODO: unlet {list}[idx]
+     // TODO: unlet {dict}[key]
+     emsg("Sorry, :unlet not fully implemented yet");
+     return FAIL;
+ }
+ 
+ /*
+  * compile "unlet var", "lock var" and "unlock var"
+  * "arg" points to "var".
+  */
+     static char_u *
+ compile_unletlock(char_u *arg, exarg_T *eap, cctx_T *cctx)
+ {
+     char_u *p = arg;
+ 
+     if (eap->cmdidx != CMD_unlet)
+     {
+       emsg("Sorry, :lock and unlock not implemented yet");
+       return NULL;
+     }
+ 
+     if (*p == '!')
+     {
+       p = skipwhite(p + 1);
+       eap->forceit = TRUE;
+     }
+ 
+     ex_unletlock(eap, p, 0, GLV_NO_AUTOLOAD, compile_unlet, cctx);
+     return eap->nextcmd == NULL ? (char_u *)"" : eap->nextcmd;
+ }
+ 
+ /*
   * Compile an :import command.
   */
      static char_u *
***************
*** 6031,6036 ****
--- 6123,6134 ----
                    line = compile_assignment(p, &ea, ea.cmdidx, &cctx);
                    break;
  
+           case CMD_unlet:
+           case CMD_unlockvar:
+           case CMD_lockvar:
+                   line = compile_unletlock(p, &ea, &cctx);
+                   break;
+ 
            case CMD_import:
                    line = compile_import(p, &cctx);
                    break;
***************
*** 6264,6269 ****
--- 6362,6371 ----
            vim_free(isn->isn_arg.loadstore.ls_name);
            break;
  
+       case ISN_UNLET:
+           vim_free(isn->isn_arg.unlet.ul_name);
+           break;
+ 
        case ISN_STOREOPT:
            vim_free(isn->isn_arg.storeopt.so_name);
            break;
*** ../vim-8.2.0600/src/proto/vim9compile.pro   2020-03-20 18:39:42.977273186 
+0100
--- src/proto/vim9compile.pro   2020-04-19 16:20:40.765331627 +0200
***************
*** 1,13 ****
  /* vim9compile.c */
  int check_defined(char_u *p, int len, cctx_T *cctx);
  char_u *skip_type(char_u *start);
! type_T *parse_type(char_u **arg, garray_T *type_list);
  char *vartype_name(vartype_T type);
  char *type_name(type_T *type, char **tofree);
  int get_script_item_idx(int sid, char_u *name, int check_writable);
  imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
  char_u *to_name_const_end(char_u *arg);
  int assignment_len(char_u *p, int *heredoc);
  void compile_def_function(ufunc_T *ufunc, int set_return_type);
  void delete_instr(isn_T *isn);
  void delete_def_function(ufunc_T *ufunc);
--- 1,14 ----
  /* vim9compile.c */
  int check_defined(char_u *p, int len, cctx_T *cctx);
  char_u *skip_type(char_u *start);
! type_T *parse_type(char_u **arg, garray_T *type_gap);
  char *vartype_name(vartype_T type);
  char *type_name(type_T *type, char **tofree);
  int get_script_item_idx(int sid, char_u *name, int check_writable);
  imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
  char_u *to_name_const_end(char_u *arg);
  int assignment_len(char_u *p, int *heredoc);
+ int check_vim9_unlet(char_u *name);
  void compile_def_function(ufunc_T *ufunc, int set_return_type);
  void delete_instr(isn_T *isn);
  void delete_def_function(ufunc_T *ufunc);
*** ../vim-8.2.0600/src/vim9.h  2020-04-19 14:32:13.556206438 +0200
--- src/vim9.h  2020-04-19 15:47:44.765833435 +0200
***************
*** 44,49 ****
--- 44,51 ----
  
      ISN_STORENR,    // store number into local variable 
isn_arg.storenr.stnr_idx
  
+     ISN_UNLET,                // unlet variable isn_arg.unlet.ul_name
+ 
      // constants
      ISN_PUSHNR,               // push number isn_arg.number
      ISN_PUSHBOOL,     // push bool value isn_arg.number
***************
*** 205,210 ****
--- 207,218 ----
      int               script_idx;     // index in sn_var_vals
  } script_T;
  
+ // arguments to ISN_UNLET
+ typedef struct {
+     char_u    *ul_name;       // variable name with g:, w:, etc.
+     int               ul_forceit;     // forceit flag
+ } unlet_T;
+ 
  /*
   * Instruction
   */
***************
*** 235,240 ****
--- 243,249 ----
        storeopt_T          storeopt;
        loadstore_T         loadstore;
        script_T            script;
+       unlet_T             unlet;
      } isn_arg;
  };
  
*** ../vim-8.2.0600/src/vim9execute.c   2020-04-19 14:32:13.556206438 +0200
--- src/vim9execute.c   2020-04-19 15:56:22.380317530 +0200
***************
*** 1068,1073 ****
--- 1068,1079 ----
                }
                break;
  
+           case ISN_UNLET:
+               if (do_unlet(iptr->isn_arg.unlet.ul_name,
+                                      iptr->isn_arg.unlet.ul_forceit) == FAIL)
+                   goto failed;
+               break;
+ 
            // create a list from items on the stack; uses a single allocation
            // for the list header and the items
            case ISN_NEWLIST:
***************
*** 2108,2113 ****
--- 2114,2124 ----
            case ISN_PUSHEXC:
                smsg("%4d PUSH v:exception", current);
                break;
+           case ISN_UNLET:
+               smsg("%4d UNLET%s %s", current,
+                       iptr->isn_arg.unlet.ul_forceit ? "!" : "",
+                       iptr->isn_arg.unlet.ul_name);
+               break;
            case ISN_NEWLIST:
                smsg("%4d NEWLIST size %lld", current,
                                            (long long)(iptr->isn_arg.number));
*** ../vim-8.2.0600/src/evalvars.c      2020-04-14 20:15:45.284566193 +0200
--- src/evalvars.c      2020-04-19 16:21:14.801284268 +0200
***************
*** 172,180 ****
  static void list_tab_vars(int *first);
  static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first);
  static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, 
char_u *endchars, char_u *op);
! static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep);
! static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit);
! static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock);
  static void item_lock(typval_T *tv, int deep, int lock);
  static void delete_var(hashtab_T *ht, hashitem_T *hi);
  static void list_one_var(dictitem_T *v, char *prefix, int *first);
--- 172,179 ----
  static void list_tab_vars(int *first);
  static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first);
  static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, 
char_u *endchars, char_u *op);
! static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, 
void *cookie);
! static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, 
void *cookie);
  static void item_lock(typval_T *tv, int deep, int lock);
  static void delete_var(hashtab_T *ht, hashitem_T *hi);
  static void list_one_var(dictitem_T *v, char *prefix, int *first);
***************
*** 1372,1378 ****
      void
  ex_unlet(exarg_T *eap)
  {
!     ex_unletlock(eap, eap->arg, 0);
  }
  
  /*
--- 1371,1377 ----
      void
  ex_unlet(exarg_T *eap)
  {
!     ex_unletlock(eap, eap->arg, 0, 0, do_unlet_var, NULL);
  }
  
  /*
***************
*** 1392,1408 ****
        arg = skipwhite(arg);
      }
  
!     ex_unletlock(eap, arg, deep);
  }
  
  /*
   * ":unlet", ":lockvar" and ":unlockvar" are quite similar.
   */
!     static void
  ex_unletlock(
      exarg_T   *eap,
      char_u    *argstart,
!     int               deep)
  {
      char_u    *arg = argstart;
      char_u    *name_end;
--- 1391,1412 ----
        arg = skipwhite(arg);
      }
  
!     ex_unletlock(eap, arg, deep, 0, do_lock_var, NULL);
  }
  
  /*
   * ":unlet", ":lockvar" and ":unlockvar" are quite similar.
+  * Also used for Vim9 script.  "callback" is invoked as:
+  *    callback(&lv, name_end, eap, deep, cookie)
   */
!     void
  ex_unletlock(
      exarg_T   *eap,
      char_u    *argstart,
!     int               deep,
!     int               glv_flags,
!     int               (*callback)(lval_T *, char_u *, exarg_T *, int, void *),
!     void      *cookie)
  {
      char_u    *arg = argstart;
      char_u    *name_end;
***************
*** 1426,1433 ****
        }
  
        // Parse the name and find the end.
!       name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, 0,
!                                                            FNE_CHECK_START);
        if (lv.ll_name == NULL)
            error = TRUE;           // error but continue parsing
        if (name_end == NULL || (!VIM_ISWHITE(*name_end)
--- 1430,1437 ----
        }
  
        // Parse the name and find the end.
!       name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error,
!                                                  glv_flags, FNE_CHECK_START);
        if (lv.ll_name == NULL)
            error = TRUE;           // error but continue parsing
        if (name_end == NULL || (!VIM_ISWHITE(*name_end)
***************
*** 1443,1468 ****
            break;
        }
  
!       if (!error && !eap->skip)
!       {
!           if (eap->cmdidx == CMD_unlet)
!           {
!               if (do_unlet_var(&lv, name_end, eap->forceit) == FAIL)
!                   error = TRUE;
!           }
!           else
!           {
!               if (do_lock_var(&lv, name_end, deep,
!                                         eap->cmdidx == CMD_lockvar) == FAIL)
!                   error = TRUE;
!           }
!       }
  
        if (!eap->skip)
            clear_lval(&lv);
  
        arg = skipwhite(name_end);
!     } while (!ends_excmd(*arg));
  
      eap->nextcmd = check_nextcmd(arg);
  }
--- 1447,1461 ----
            break;
        }
  
!       if (!error && !eap->skip
!                        && callback(&lv, name_end, eap, deep, cookie) == FAIL)
!           error = TRUE;
  
        if (!eap->skip)
            clear_lval(&lv);
  
        arg = skipwhite(name_end);
!     } while (!ends_excmd2(name_end, arg));
  
      eap->nextcmd = check_nextcmd(arg);
  }
***************
*** 1471,1478 ****
  do_unlet_var(
      lval_T    *lp,
      char_u    *name_end,
!     int               forceit)
  {
      int               ret = OK;
      int               cc;
  
--- 1464,1474 ----
  do_unlet_var(
      lval_T    *lp,
      char_u    *name_end,
!     exarg_T   *eap,
!     int               deep UNUSED,
!     void      *cookie UNUSED)
  {
+     int               forceit = eap->forceit;
      int               ret = OK;
      int               cc;
  
***************
*** 1541,1546 ****
--- 1537,1546 ----
      dict_T    *d;
      dictitem_T        *di;
  
+     if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
+           && check_vim9_unlet(name) == FAIL)
+       return FAIL;
+ 
      ht = find_var_ht(name, &varname);
      if (ht != NULL && *varname != NUL)
      {
***************
*** 1592,1600 ****
  do_lock_var(
      lval_T    *lp,
      char_u    *name_end,
      int               deep,
!     int               lock)
  {
      int               ret = OK;
      int               cc;
      dictitem_T        *di;
--- 1592,1602 ----
  do_lock_var(
      lval_T    *lp,
      char_u    *name_end,
+     exarg_T   *eap,
      int               deep,
!     void      *cookie UNUSED)
  {
+     int               lock = eap->cmdidx == CMD_lockvar;
      int               ret = OK;
      int               cc;
      dictitem_T        *di;
*** ../vim-8.2.0600/src/proto/evalvars.pro      2020-04-14 20:15:45.284566193 
+0200
--- src/proto/evalvars.pro      2020-04-19 15:38:44.039777011 +0200
***************
*** 21,26 ****
--- 21,27 ----
  void list_hashtable_vars(hashtab_T *ht, char *prefix, int empty, int *first);
  void ex_unlet(exarg_T *eap);
  void ex_lockvar(exarg_T *eap);
+ void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, 
int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie);
  int do_unlet(char_u *name, int forceit);
  void del_menutrans_vars(void);
  char_u *get_user_var_name(expand_T *xp, int idx);
*** ../vim-8.2.0600/src/eval.c  2020-04-16 22:54:28.723107683 +0200
--- src/eval.c  2020-04-19 15:36:14.216123215 +0200
***************
*** 5098,5103 ****
--- 5098,5104 ----
      int               br_nest = 0;
      char_u    *p;
      int               len;
+     int               vim9script = current_sctx.sc_version == 
SCRIPT_VERSION_VIM9;
  
      if (expr_start != NULL)
      {
***************
*** 5106,5117 ****
      }
  
      // Quick check for valid starting character.
!     if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg) && *arg != '{')
        return arg;
  
      for (p = arg; *p != NUL
                    && (eval_isnamec(*p)
!                       || *p == '{'
                        || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.'))
                        || mb_nest != 0
                        || br_nest != 0); MB_PTR_ADV(p))
--- 5107,5119 ----
      }
  
      // Quick check for valid starting character.
!     if ((flags & FNE_CHECK_START) && !eval_isnamec1(*arg)
!                                               && (*arg != '{' || vim9script))
        return arg;
  
      for (p = arg; *p != NUL
                    && (eval_isnamec(*p)
!                       || (*p == '{' && !vim9script)
                        || ((flags & FNE_INCL_BR) && (*p == '[' || *p == '.'))
                        || mb_nest != 0
                        || br_nest != 0); MB_PTR_ADV(p))
***************
*** 5151,5157 ****
                --br_nest;
        }
  
!       if (br_nest == 0)
        {
            if (*p == '{')
            {
--- 5153,5159 ----
                --br_nest;
        }
  
!       if (br_nest == 0 && !vim9script)
        {
            if (*p == '{')
            {
*** ../vim-8.2.0600/src/testdir/test_vim9_script.vim    2020-04-19 
14:32:13.556206438 +0200
--- src/testdir/test_vim9_script.vim    2020-04-19 16:01:04.171549078 +0200
***************
*** 213,219 ****
    return 'xxx'
  enddef
  
! func Test_assignment_failure()
    call CheckDefFailure(['let var=234'], 'E1004:')
    call CheckDefFailure(['let var =234'], 'E1004:')
    call CheckDefFailure(['let var= 234'], 'E1004:')
--- 213,219 ----
    return 'xxx'
  enddef
  
! def Test_assignment_failure()
    call CheckDefFailure(['let var=234'], 'E1004:')
    call CheckDefFailure(['let var =234'], 'E1004:')
    call CheckDefFailure(['let var= 234'], 'E1004:')
***************
*** 241,249 ****
    call CheckDefFailure(['let xnr += 4'], 'E1020:')
  
    call CheckScriptFailure(['vim9script', 'def Func()', 'let dummy = 
s:notfound', 'enddef'], 'E1050:')
-   " TODO: implement this error
-   "call CheckScriptFailure(['vim9script', 'let svar = 123', 'unlet svar'], 
'E1050:')
-   "call CheckScriptFailure(['vim9script', 'let svar = 123', 'unlet s:svar'], 
'E1050:')
  
    call CheckDefFailure(['let var: list<string> = [123]'], 'expected 
list<string> but got list<number>')
    call CheckDefFailure(['let var: list<number> = ["xx"]'], 'expected 
list<number> but got list<string>')
--- 241,246 ----
***************
*** 259,265 ****
  
    call assert_fails('s/^/\=Mess()/n', 'E794:')
    call CheckDefFailure(['let var: dict<number'], 'E1009:')
! endfunc
  
  func Test_wrong_type()
    call CheckDefFailure(['let var: list<nothing>'], 'E1010:')
--- 256,295 ----
  
    call assert_fails('s/^/\=Mess()/n', 'E794:')
    call CheckDefFailure(['let var: dict<number'], 'E1009:')
! enddef
! 
! def Test_unlet()
!   g:somevar = 'yes'
!   assert_true(exists('g:somevar'))
!   unlet g:somevar
!   assert_false(exists('g:somevar'))
!   unlet! g:somevar
! 
!   call CheckScriptFailure([
!         'vim9script',
!         'let svar = 123',
!         'unlet svar',
!         ], 'E1081:')
!   call CheckScriptFailure([
!         'vim9script',
!         'let svar = 123',
!         'unlet s:svar',
!         ], 'E1081:')
!   call CheckScriptFailure([
!         'vim9script',
!         'let svar = 123',
!         'def Func()',
!         '  unlet svar',
!         'enddef',
!         ], 'E1081:')
!   call CheckScriptFailure([
!         'vim9script',
!         'let svar = 123',
!         'def Func()',
!         '  unlet s:svar',
!         'enddef',
!         ], 'E1081:')
! enddef
  
  func Test_wrong_type()
    call CheckDefFailure(['let var: list<nothing>'], 'E1010:')
***************
*** 1155,1160 ****
--- 1185,1208 ----
  
    au! TabEnter
    unlet g:entered
+ 
+   CheckScriptSuccess([
+       'vim9script',
+       'let g:var = 123',
+       'let w:var = 777',
+       'unlet g:var w:var # something',
+       ])
+ 
+   CheckScriptFailure([
+       'vim9script',
+       'let g:var = 123',
+       'unlet g:var# comment',
+       ], 'E108:')
+ 
+   CheckScriptFailure([
+       'let g:var = 123',
+       'unlet g:var # something',
+       ], 'E488:')
  enddef
  
  " Keep this last, it messes up highlighting.
*** ../vim-8.2.0600/src/testdir/test_vim9_disassemble.vim       2020-04-19 
14:32:13.556206438 +0200
--- src/testdir/test_vim9_disassemble.vim       2020-04-19 16:16:30.837605121 
+0200
***************
*** 126,131 ****
--- 126,150 ----
          res)
  enddef
  
+ def s:ScriptFuncUnlet()
+   g:somevar = "value"
+   unlet g:somevar
+   unlet! g:somevar
+ enddef
+ 
+ def Test_disassemble_unlet()
+   let res = execute('disass s:ScriptFuncUnlet')
+   assert_match('<SNR>\d*_ScriptFuncUnlet.*' ..
+         'g:somevar = "value".*' ..
+         '\d PUSHS "value".*' ..
+         '\d STOREG g:somevar.*' ..
+         'unlet g:somevar.*' ..
+         '\d UNLET g:somevar.*' ..
+         'unlet! g:somevar.*' ..
+         '\d UNLET! g:somevar.*',
+         res)
+ enddef
+ 
  def s:ScriptFuncTry()
    try
      echo 'yes'
*** ../vim-8.2.0600/src/version.c       2020-04-19 14:32:13.556206438 +0200
--- src/version.c       2020-04-19 14:50:53.650513615 +0200
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     601,
  /**/

-- 
A salesperson says:            Translation:
"backward compatible"          Old technology
"Premium"                      Overpriced
"Can't keep it on the shelf"   Unavailable
"Stands alone"                 Piece of shit
"Proprietary"                  Incompatible
                                (Scott Adams - The Dilbert principle)

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            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/202004191429.03JETT75030397%40masaka.moolenaar.net.

Raspunde prin e-mail lui