changeset 26470:ff0310e6f889 v8.2.3765

patch 8.2.3765: Vim9: cannot use a lambda for 'opfunc' and others Commit: https://github.com/vim/vim/commit/dcb53be4418fe263a71c7738315241031df6c986 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Dec 9 14:23:43 2021 +0000 patch 8.2.3765: Vim9: cannot use a lambda for 'opfunc' and others Problem: Vim9: cannot use a lambda for 'opfunc' and others. Solution: Convert the lambda to a string.
author Bram Moolenaar <Bram@vim.org>
date Thu, 09 Dec 2021 15:30:04 +0100
parents 8e3d1d2500bb
children d76f275bc6dc
files src/testdir/test_vim9_disassemble.vim src/testdir/test_vim9_func.vim src/version.c src/vim9.h src/vim9compile.c src/vim9execute.c
diffstat 6 files changed, 92 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -316,6 +316,7 @@ def s:ScriptFuncStore()
   w:windowvar = 'wv'
   t:tabpagevar = 'tv'
   &tabstop = 8
+  &opfunc = (t) => len(t)
   $ENVVAR = 'ev'
   @z = 'rv'
 enddef
@@ -343,12 +344,17 @@ def Test_disassemble_store()
         ' STOREW w:windowvar.*' ..
         't:tabpagevar = ''tv''.*' ..
         ' STORET t:tabpagevar.*' ..
-        '&tabstop = 8.*' ..
-        ' STOREOPT &tabstop.*' ..
-        '$ENVVAR = ''ev''.*' ..
-        ' STOREENV $ENVVAR.*' ..
+        '&tabstop = 8\_s*' ..
+        '\d\+ PUSHNR 8\_s*' ..
+        '\d\+ STOREOPT &tabstop\_s*' ..
+        '&opfunc = (t) => len(t)\_s*' ..
+        '\d\+ FUNCREF <lambda>\d\+\_s*' ..
+        '\d\+ STOREFUNCOPT &opfunc\_s*' ..
+        '$ENVVAR = ''ev''\_s*' ..
+        '\d\+ PUSHS "ev"\_s*' ..
+        '\d\+ STOREENV $ENVVAR\_s*' ..
         '@z = ''rv''.*' ..
-        ' STOREREG @z.*',
+        '\d\+ STOREREG @z.*',
         res)
 enddef
 
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -1202,6 +1202,28 @@ def Test_lambda_in_reduce_line_break()
   CheckScriptSuccess(lines)
 enddef
 
+def Test_set_opfunc_to_lambda()
+  var lines =<< trim END
+    vim9script
+    nnoremap <expr> <F4> <SID>CountSpaces() .. '_'
+    def CountSpaces(type = ''): string
+      if type == ''
+        &operatorfunc = (t) => CountSpaces(t)
+        return 'g@'
+      endif
+      normal! '[V']y
+      g:result = getreg('"')->count(' ')
+      return ''
+    enddef
+    new
+    'a b c d e'->setline(1)
+    feedkeys("\<F4>", 'x')
+    assert_equal(4, g:result)
+    bwipe!
+  END
+  CheckScriptSuccess(lines)
+enddef
+
 " Default arg and varargs
 def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
   var res = one .. ',' .. two
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    3765,
+/**/
     3764,
 /**/
     3763,
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -55,7 +55,8 @@ typedef enum {
     ISN_STORES,	    // pop into script variable isn_arg.loadstore
     ISN_STOREOUTER,  // pop variable into outer scope isn_arg.outer
     ISN_STORESCRIPT, // pop into script variable isn_arg.script
-    ISN_STOREOPT,    // pop into option isn_arg.string
+    ISN_STOREOPT,    // pop into option isn_arg.storeopt
+    ISN_STOREFUNCOPT, // pop into option isn_arg.storeopt
     ISN_STOREENV,    // pop into environment variable isn_arg.string
     ISN_STOREREG,    // pop into register isn_arg.number
     // ISN_STOREOTHER, // pop into other script variable isn_arg.other.
@@ -291,7 +292,7 @@ typedef struct {
     varnumber_T	stnr_val;
 } storenr_T;
 
-// arguments to ISN_STOREOPT
+// arguments to ISN_STOREOPT and ISN_STOREFUNCOPT
 typedef struct {
     char_u	*so_name;
     int		so_flags;
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -118,6 +118,7 @@ typedef struct {
 typedef enum {
     dest_local,
     dest_option,
+    dest_func_option,
     dest_env,
     dest_global,
     dest_buffer,
@@ -1412,15 +1413,19 @@ generate_STORENR(cctx_T *cctx, int idx, 
 }
 
 /*
- * Generate an ISN_STOREOPT instruction
- */
-    static int
-generate_STOREOPT(cctx_T *cctx, char_u *name, int opt_flags)
+ * Generate an ISN_STOREOPT or ISN_STOREFUNCOPT instruction
+ */
+    static int
+generate_STOREOPT(
+	cctx_T	    *cctx,
+	isntype_T   isn_type,
+	char_u	    *name,
+	int	    opt_flags)
 {
     isn_T	*isn;
 
     RETURN_OK_IF_SKIP(cctx);
-    if ((isn = generate_instr_drop(cctx, ISN_STOREOPT, 1)) == NULL)
+    if ((isn = generate_instr_drop(cctx, isn_type, 1)) == NULL)
 	return FAIL;
     isn->isn_arg.storeopt.so_name = vim_strsave(name);
     isn->isn_arg.storeopt.so_flags = opt_flags;
@@ -5980,6 +5985,7 @@ generate_loadvar(
     switch (dest)
     {
 	case dest_option:
+	case dest_func_option:
 	    generate_LOAD(cctx, ISN_LOADOPT, 0, name, type);
 	    break;
 	case dest_global:
@@ -6094,6 +6100,7 @@ get_var_dest(
 	int		cc;
 	long		numval;
 	getoption_T	opt_type;
+	int		opt_p_flags;
 
 	*dest = dest_option;
 	if (cmdidx == CMD_final || cmdidx == CMD_const)
@@ -6112,7 +6119,7 @@ get_var_dest(
 	cc = *p;
 	*p = NUL;
 	opt_type = get_option_value(skip_option_env_lead(name),
-					   &numval, NULL, NULL, *option_scope);
+				   &numval, NULL, &opt_p_flags, *option_scope);
 	*p = cc;
 	switch (opt_type)
 	{
@@ -6121,7 +6128,16 @@ get_var_dest(
 		    return FAIL;
 	    case gov_string:
 	    case gov_hidden_string:
-		    *type = &t_string;
+		    if (opt_p_flags & P_FUNC)
+		    {
+			// might be a Funcref, check the type later
+			*type = &t_any;
+			*dest = dest_func_option;
+		    }
+		    else
+		    {
+			*type = &t_string;
+		    }
 		    break;
 	    case gov_bool:
 	    case gov_hidden_bool:
@@ -6204,8 +6220,11 @@ generate_store_var(
     switch (dest)
     {
 	case dest_option:
-	    return generate_STOREOPT(cctx, skip_option_env_lead(name),
-								    opt_flags);
+	    return generate_STOREOPT(cctx, ISN_STOREOPT,
+					skip_option_env_lead(name), opt_flags);
+	case dest_func_option:
+	    return generate_STOREOPT(cctx, ISN_STOREFUNCOPT,
+					skip_option_env_lead(name), opt_flags);
 	case dest_global:
 	    // include g: with the name, easier to execute that way
 	    return generate_STORE(cctx, vim_strchr(name, AUTOLOAD_CHAR) == NULL
@@ -6468,7 +6487,7 @@ compile_lhs(
     if (lhs->lhs_varlen > 1 || var_start[lhs->lhs_varlen] != ':')
 	var_end = lhs->lhs_dest_end;
 
-    if (lhs->lhs_dest != dest_option)
+    if (lhs->lhs_dest != dest_option && lhs->lhs_dest != dest_func_option)
     {
 	if (is_decl && *var_end == ':')
 	{
@@ -7223,7 +7242,8 @@ compile_assignment(char_u *arg, exarg_T 
 		emsg(_(e_const_requires_a_value));
 		goto theend;
 	    }
-	    else if (!lhs.lhs_has_type || lhs.lhs_dest == dest_option)
+	    else if (!lhs.lhs_has_type || lhs.lhs_dest == dest_option
+					   || lhs.lhs_dest == dest_func_option)
 	    {
 		emsg(_(e_type_or_initialization_required));
 		goto theend;
@@ -10454,6 +10474,7 @@ delete_instr(isn_T *isn)
 	    break;
 
 	case ISN_STOREOPT:
+	case ISN_STOREFUNCOPT:
 	    vim_free(isn->isn_arg.storeopt.so_name);
 	    break;
 
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2336,10 +2336,14 @@ exec_instructions(ectx_T *ectx)
 
 	    // store option
 	    case ISN_STOREOPT:
+	    case ISN_STOREFUNCOPT:
 		{
+		    char_u	*opt_name = iptr->isn_arg.storeopt.so_name;
+		    int		opt_flags = iptr->isn_arg.storeopt.so_flags;
 		    long	n = 0;
 		    char_u	*s = NULL;
 		    char	*msg;
+		    callback_T	cb = {NULL, NULL, 0};
 
 		    --ectx->ec_stack.ga_len;
 		    tv = STACK_TV_BOT(0);
@@ -2349,11 +2353,22 @@ exec_instructions(ectx_T *ectx)
 			if (s == NULL)
 			    s = (char_u *)"";
 		    }
+		    else if (iptr->isn_type == ISN_STOREFUNCOPT)
+		    {
+			SOURCING_LNUM = iptr->isn_lnum;
+			cb = get_callback(tv);
+			if (cb.cb_name == NULL || *cb.cb_name == NUL)
+			{
+			    clear_tv(tv);
+			    free_callback(&cb);
+			    goto on_error;
+			}
+			s = cb.cb_name;
+		    }
 		    else
 			// must be VAR_NUMBER, CHECKTYPE makes sure
 			n = tv->vval.v_number;
-		    msg = set_option_value(iptr->isn_arg.storeopt.so_name,
-					n, s, iptr->isn_arg.storeopt.so_flags);
+		    msg = set_option_value(opt_name, n, s, opt_flags);
 		    clear_tv(tv);
 		    if (msg != NULL)
 		    {
@@ -2361,6 +2376,8 @@ exec_instructions(ectx_T *ectx)
 			emsg(_(msg));
 			goto on_error;
 		    }
+		    if (cb.cb_name != NULL)
+			free_callback(&cb);
 		}
 		break;
 
@@ -5335,7 +5352,9 @@ list_instructions(char *pfx, isn_T *inst
 		}
 		break;
 	    case ISN_STOREOPT:
-		smsg("%s%4d STOREOPT &%s", pfx, current,
+	    case ISN_STOREFUNCOPT:
+		smsg("%s%4d %s &%s", pfx, current,
+		  iptr->isn_type == ISN_STOREOPT ? "STOREOPT" : "STOREFUNCOPT",
 					       iptr->isn_arg.storeopt.so_name);
 		break;
 	    case ISN_STOREENV: