patch 7.4.2090
Problem:    Using submatch() in a lambda passed to substitute() is verbose.
Solution:   Use a static list and pass it as an optional argument to the
            function.  Fix memory leak.
diff --git a/src/channel.c b/src/channel.c
index ae5bdf2..722dcb8 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -1533,8 +1533,8 @@
     argv[0].v_type = VAR_CHANNEL;
     argv[0].vval.v_channel = channel;
 
-    call_func(callback, (int)STRLEN(callback),
-			&rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL);
+    call_func(callback, (int)STRLEN(callback), &rettv, 2, argv, NULL,
+					  0L, 0L, &dummy, TRUE, partial, NULL);
     clear_tv(&rettv);
     channel_need_redraw = TRUE;
 }
@@ -2695,7 +2695,7 @@
 	      argv[0].v_type = VAR_CHANNEL;
 	      argv[0].vval.v_channel = channel;
 	      call_func(channel->ch_close_cb, (int)STRLEN(channel->ch_close_cb),
-			   &rettv, 1, argv, 0L, 0L, &dummy, TRUE,
+			   &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
 			   channel->ch_close_partial, NULL);
 	      clear_tv(&rettv);
 	      channel_need_redraw = TRUE;
@@ -4758,7 +4758,7 @@
 	    argv[1].v_type = VAR_NUMBER;
 	    argv[1].vval.v_number = job->jv_exitval;
 	    call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb),
-			   &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
+			   &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
 			   job->jv_exit_partial, NULL);
 	    clear_tv(&rettv);
 	    --job->jv_refcount;
diff --git a/src/eval.c b/src/eval.c
index 2ba90f9..53c188b 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -988,7 +988,7 @@
     }
 
     rettv->v_type = VAR_UNKNOWN;		/* clear_tv() uses this */
-    ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
+    ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, NULL,
 		    curwin->w_cursor.lnum, curwin->w_cursor.lnum,
 		    &doesrange, TRUE, NULL, NULL);
     if (safe)
@@ -9930,8 +9930,8 @@
     if (expr->v_type == VAR_FUNC)
     {
 	s = expr->vval.v_string;
-	if (call_func(s, (int)STRLEN(s),
-		    &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL)
+	if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
+				     0L, 0L, &dummy, TRUE, NULL, NULL) == FAIL)
 	    goto theend;
     }
     else if (expr->v_type == VAR_PARTIAL)
@@ -9939,9 +9939,8 @@
 	partial_T   *partial = expr->vval.v_partial;
 
 	s = partial->pt_name;
-	if (call_func(s, (int)STRLEN(s),
-		    &rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL)
-								      == FAIL)
+	if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
+				  0L, 0L, &dummy, TRUE, partial, NULL) == FAIL)
 	    goto theend;
     }
     else
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 54b5a53..61dce74 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -10101,7 +10101,7 @@
 
     rettv.v_type = VAR_UNKNOWN;		/* clear_tv() uses this */
     res = call_func(func_name, (int)STRLEN(func_name),
-				 &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
+				 &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE,
 				 partial, sortinfo->item_compare_selfdict);
     clear_tv(&argv[0]);
     clear_tv(&argv[1]);
diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c
index 2b2d635..adc5352 100644
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -1163,7 +1163,7 @@
     argv[1].v_type = VAR_UNKNOWN;
 
     call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
-			&rettv, 1, argv, 0L, 0L, &dummy, TRUE,
+			&rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
 			timer->tr_partial, NULL);
     clear_tv(&rettv);
 }
diff --git a/src/list.c b/src/list.c
index 13f9200..9e0e26f 100644
--- a/src/list.c
+++ b/src/list.c
@@ -924,4 +924,35 @@
     return ret;
 }
 
+/*
+ * Initialize a static list with 10 items.
+ */
+    void
+init_static_list(staticList10_T *sl)
+{
+    list_T  *l = &sl->sl_list;
+    int	    i;
+
+    memset(sl, 0, sizeof(staticList10_T));
+    l->lv_first = &sl->sl_items[0];
+    l->lv_last = &sl->sl_items[9];
+    l->lv_refcount = DO_NOT_FREE_CNT;
+    l->lv_lock = VAR_FIXED;
+    sl->sl_list.lv_len = 10;
+
+    for (i = 0; i < 10; ++i)
+    {
+	listitem_T *li = &sl->sl_items[i];
+
+	if (i == 0)
+	    li->li_prev = NULL;
+	else
+	    li->li_prev = li - 1;
+	if (i == 9)
+	    li->li_next = NULL;
+	else
+	    li->li_next = li + 1;
+    }
+}
+
 #endif /* defined(FEAT_EVAL) */
diff --git a/src/proto/list.pro b/src/proto/list.pro
index 9849379..56f0ddc 100644
--- a/src/proto/list.pro
+++ b/src/proto/list.pro
@@ -32,4 +32,5 @@
 int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID);
 int get_list_tv(char_u **arg, typval_T *rettv, int evaluate);
 int write_list(FILE *fd, list_T *list, int binary);
+void init_static_list(staticList10_T *sl);
 /* vim: set ft=c : */
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index b354197..feacd4c 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -5,7 +5,7 @@
 int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict);
 void free_all_functions(void);
 int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv);
-int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in);
+int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, int (*argv_func)(int, typval_T *, int), linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in);
 void ex_function(exarg_T *eap);
 int eval_fname_script(char_u *p);
 int translated_function_exists(char_u *name);
diff --git a/src/regexp.c b/src/regexp.c
index 1a77758..6b23cfb 100644
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -7290,6 +7290,50 @@
 #endif
 
 #if defined(FEAT_MODIFY_FNAME) || defined(FEAT_EVAL) || defined(PROTO)
+
+/*
+ * Put the submatches in "argv[0]" which is a list passed into call_func() by
+ * vim_regsub_both().
+ */
+    static int
+fill_submatch_list(int argc UNUSED, typval_T *argv, int argcount)
+{
+    listitem_T	*li;
+    int		i;
+    char_u	*s;
+
+    if (argcount == 0)
+	/* called function doesn't take an argument */
+	return 0;
+
+    /* Relies on sl_list to be the first item in staticList10_T. */
+    init_static_list((staticList10_T *)(argv->vval.v_list));
+
+    /* There are always 10 list items in staticList10_T. */
+    li = argv->vval.v_list->lv_first;
+    for (i = 0; i < 10; ++i)
+    {
+	s = submatch_match->startp[i];
+	if (s == NULL || submatch_match->endp[i] == NULL)
+	    s = NULL;
+	else
+	    s = vim_strnsave(s, (int)(submatch_match->endp[i] - s));
+	li->li_tv.v_type = VAR_STRING;
+	li->li_tv.vval.v_string = s;
+	li = li->li_next;
+    }
+    return 1;
+}
+
+    static void
+clear_submatch_list(staticList10_T *sl)
+{
+    int i;
+
+    for (i = 0; i < 10; ++i)
+	vim_free(sl->sl_items[i].li_tv.vval.v_string);
+}
+
 /*
  * vim_regsub() - perform substitutions after a vim_regexec() or
  * vim_regexec_multi() match.
@@ -7427,10 +7471,11 @@
 
 	    if (expr != NULL)
 	    {
-		typval_T	argv[1];
+		typval_T	argv[2];
 		int		dummy;
 		char_u		buf[NUMBUFLEN];
 		typval_T	rettv;
+		staticList10_T	matchList;
 
 		rettv.v_type = VAR_STRING;
 		rettv.vval.v_string = NULL;
@@ -7438,23 +7483,35 @@
 		{
 		    /* can't do this recursively */
 		}
-		else if (expr->v_type == VAR_FUNC)
+		else
 		{
-		    s = expr->vval.v_string;
-		    call_func(s, (int)STRLEN(s), &rettv, 0, argv,
+		    argv[0].v_type = VAR_LIST;
+		    argv[0].vval.v_list = &matchList.sl_list;
+		    matchList.sl_list.lv_len = 0;
+		    if (expr->v_type == VAR_FUNC)
+		    {
+			s = expr->vval.v_string;
+			call_func(s, (int)STRLEN(s), &rettv,
+					1, argv, fill_submatch_list,
 					     0L, 0L, &dummy, TRUE, NULL, NULL);
-		}
-		else if (expr->v_type == VAR_PARTIAL)
-		{
-		    partial_T   *partial = expr->vval.v_partial;
+		    }
+		    else if (expr->v_type == VAR_PARTIAL)
+		    {
+			partial_T   *partial = expr->vval.v_partial;
 
-		    s = partial->pt_name;
-		    call_func(s, (int)STRLEN(s), &rettv, 0, argv,
+			s = partial->pt_name;
+			call_func(s, (int)STRLEN(s), &rettv,
+					1, argv, fill_submatch_list,
 					  0L, 0L, &dummy, TRUE, partial, NULL);
+		    }
+		    if (matchList.sl_list.lv_len > 0)
+			/* fill_submatch_list() was called */
+			clear_submatch_list(&matchList);
 		}
 		eval_result = get_tv_string_buf_chk(&rettv, buf);
 		if (eval_result != NULL)
 		    eval_result = vim_strsave(eval_result);
+		clear_tv(&rettv);
 	    }
 	    else
 		eval_result = eval_to_string(source + 2, NULL, TRUE);
diff --git a/src/structs.h b/src/structs.h
index 191fa1f..7375c7c 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1245,6 +1245,14 @@
 };
 
 /*
+ * Static list with 10 items.  Use init_static_list() to initialize.
+ */
+typedef struct {
+    list_T	sl_list;	/* must be first */
+    listitem_T	sl_items[10];
+} staticList10_T;
+
+/*
  * Structure to hold an item of a Dictionary.
  * Also used for a variable.
  * The key is copied into "di_key" to avoid an extra alloc/free for it.
diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim
index 235dd8d..8db4d8e 100644
--- a/src/testdir/test_expr.vim
+++ b/src/testdir/test_expr.vim
@@ -153,3 +153,22 @@
   endfunc
   call assert_equal('--', substitute('xxx', 'x*', {-> '-' . Recurse() . '-'}, ''))
 endfunc
+
+func Test_substitute_expr_arg()
+  call assert_equal('123456789-123456789=', substitute('123456789',
+	\ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
+	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+  call assert_equal('123456-123456=789', substitute('123456789',
+	\ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
+	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+  call assert_equal('123456789-123456789x=', substitute('123456789',
+	\ '\(.\)\(.\)\(.*\)',
+	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+  call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
+  call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
+  call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
+  call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
+endfunc
diff --git a/src/userfunc.c b/src/userfunc.c
index ffbbc2d..af1863f 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -480,7 +480,7 @@
 								  &argvars[i];
 	}
 
-	ret = call_func(name, len, rettv, argcount, argvars,
+	ret = call_func(name, len, rettv, argcount, argvars, NULL,
 		 firstline, lastline, doesrange, evaluate, partial, selfdict);
 
 	funcargs.ga_len -= i;
@@ -1139,7 +1139,7 @@
     }
 
     if (item == NULL)
-	r = call_func(name, (int)STRLEN(name), rettv, argc, argv,
+	r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL,
 				 curwin->w_cursor.lnum, curwin->w_cursor.lnum,
 					     &dummy, TRUE, partial, selfdict);
 
@@ -1152,6 +1152,11 @@
 
 /*
  * Call a function with its resolved parameters
+ *
+ * "argv_func", when not NULL, can be used to fill in arguments only when the
+ * invoked function uses them.  It is called like this:
+ *   new_argcount = argv_func(current_argcount, argv, called_func_argcount)
+ *
  * Return FAIL when the function can't be called,  OK otherwise.
  * Also returns OK when an error was encountered while executing the function.
  */
@@ -1163,6 +1168,8 @@
     int		argcount_in,	/* number of "argvars" */
     typval_T	*argvars_in,	/* vars for arguments, must have "argcount"
 				   PLUS ONE elements! */
+    int		(* argv_func)(int, typval_T *, int),
+				/* function to fill in argvars */
     linenr_T	firstline,	/* first line of range */
     linenr_T	lastline,	/* last line of range */
     int		*doesrange,	/* return: function handled range */
@@ -1254,6 +1261,9 @@
 
 	    if (fp != NULL)
 	    {
+		if (argv_func != NULL)
+		    argcount = argv_func(argcount, argvars, fp->uf_args.ga_len);
+
 		if (fp->uf_flags & FC_RANGE)
 		    *doesrange = TRUE;
 		if (argcount < fp->uf_args.ga_len)
diff --git a/src/version.c b/src/version.c
index dccf2ce..e42229e 100644
--- a/src/version.c
+++ b/src/version.c
@@ -759,6 +759,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2090,
+/**/
     2089,
 /**/
     2088,