diff src/eval.c @ 8538:c337c813c64d v7.4.1559

commit https://github.com/vim/vim/commit/1735bc988c546cc962c5f94792815b4d7cb79710 Author: Bram Moolenaar <Bram@vim.org> Date: Mon Mar 14 23:05:14 2016 +0100 patch 7.4.1559 Problem: Passing cookie to a callback is clumsy. Solution: Change function() to take arguments and return a partial.
author Christian Brabandt <cb@256bit.org>
date Mon, 14 Mar 2016 23:15:05 +0100
parents 09041d2fd7d0
children 24db3583c496
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -452,8 +452,8 @@ static char_u *tv2string(typval_T *tv, c
 static char_u *string_quote(char_u *str, int function);
 static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate);
 static int find_internal_func(char_u *name);
-static char_u *deref_func_name(char_u *name, int *lenp, int no_autoload);
-static 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, dict_T *selfdict);
+static char_u *deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload);
+static 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);
 static void emsg_funcname(char *ermsg, char_u *name);
 static int non_zero_arg(typval_T *argvars);
 
@@ -1675,7 +1675,7 @@ call_vim_function(
     rettv->v_type = VAR_UNKNOWN;		/* clear_tv() uses this */
     ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
 		    curwin->w_cursor.lnum, curwin->w_cursor.lnum,
-		    &doesrange, TRUE, NULL);
+		    &doesrange, TRUE, NULL, NULL);
     if (safe)
     {
 	--sandbox;
@@ -3091,6 +3091,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char
 	    case VAR_UNKNOWN:
 	    case VAR_DICT:
 	    case VAR_FUNC:
+	    case VAR_PARTIAL:
 	    case VAR_SPECIAL:
 	    case VAR_JOB:
 	    case VAR_CHANNEL:
@@ -3456,6 +3457,7 @@ ex_call(exarg_T *eap)
     int		doesrange;
     int		failed = FALSE;
     funcdict_T	fudi;
+    partial_T	*partial;
 
     if (eap->skip)
     {
@@ -3486,7 +3488,7 @@ ex_call(exarg_T *eap)
 
     /* If it is the name of a variable of type VAR_FUNC use its contents. */
     len = (int)STRLEN(tofree);
-    name = deref_func_name(tofree, &len, FALSE);
+    name = deref_func_name(tofree, &len, &partial, FALSE);
 
     /* Skip white space to allow ":call func ()".  Not good, but required for
      * backward compatibility. */
@@ -3525,7 +3527,7 @@ ex_call(exarg_T *eap)
 	arg = startarg;
 	if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg,
 		    eap->line1, eap->line2, &doesrange,
-					    !eap->skip, fudi.fd_dict) == FAIL)
+				   !eap->skip, partial, fudi.fd_dict) == FAIL)
 	{
 	    failed = TRUE;
 	    break;
@@ -3870,6 +3872,7 @@ item_lock(typval_T *tv, int deep, int lo
 	case VAR_NUMBER:
 	case VAR_STRING:
 	case VAR_FUNC:
+	case VAR_PARTIAL:
 	case VAR_FLOAT:
 	case VAR_SPECIAL:
 	case VAR_JOB:
@@ -4542,7 +4545,8 @@ eval4(char_u **arg, typval_T *rettv, int
 		}
 	    }
 
-	    else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC)
+	    else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC
+		|| rettv->v_type == VAR_PARTIAL || var2.v_type == VAR_PARTIAL)
 	    {
 		if (rettv->v_type != var2.v_type
 			|| (type != TYPE_EQUAL && type != TYPE_NEQUAL))
@@ -4555,6 +4559,12 @@ eval4(char_u **arg, typval_T *rettv, int
 		    clear_tv(&var2);
 		    return FAIL;
 		}
+		else if (rettv->v_type == VAR_PARTIAL)
+		{
+		    /* Partials are only equal when identical. */
+		    n1 = rettv->vval.v_partial != NULL
+			      && rettv->vval.v_partial == var2.vval.v_partial;
+		}
 		else
 		{
 		    /* Compare two Funcrefs for being equal or unequal. */
@@ -4564,9 +4574,9 @@ eval4(char_u **arg, typval_T *rettv, int
 		    else
 			n1 = STRCMP(rettv->vval.v_string,
 						     var2.vval.v_string) == 0;
-		    if (type == TYPE_NEQUAL)
-			n1 = !n1;
-		}
+		}
+		if (type == TYPE_NEQUAL)
+		    n1 = !n1;
 	    }
 
 #ifdef FEAT_FLOAT
@@ -5230,14 +5240,16 @@ eval7(
 	{
 	    if (**arg == '(')		/* recursive! */
 	    {
+		partial_T *partial;
+
 		/* If "s" is the name of a variable of type VAR_FUNC
 		 * use its contents. */
-		s = deref_func_name(s, &len, !evaluate);
+		s = deref_func_name(s, &len, &partial, !evaluate);
 
 		/* Invoke the function. */
 		ret = get_func_tv(s, len, rettv, arg,
 			  curwin->w_cursor.lnum, curwin->w_cursor.lnum,
-			  &len, evaluate, NULL);
+			  &len, evaluate, partial, NULL);
 
 		/* If evaluate is FALSE rettv->v_type was not set in
 		 * get_func_tv, but it's needed in handle_subscript() to parse
@@ -5359,6 +5371,7 @@ eval_index(
     switch (rettv->v_type)
     {
 	case VAR_FUNC:
+	case VAR_PARTIAL:
 	    if (verbose)
 		EMSG(_("E695: Cannot index a Funcref"));
 	    return FAIL;
@@ -5480,6 +5493,7 @@ eval_index(
 	{
 	    case VAR_UNKNOWN:
 	    case VAR_FUNC:
+	    case VAR_PARTIAL:
 	    case VAR_FLOAT:
 	    case VAR_SPECIAL:
 	    case VAR_JOB:
@@ -6218,6 +6232,10 @@ tv_equal(
 		    && tv2->vval.v_string != NULL
 		    && STRCMP(tv1->vval.v_string, tv2->vval.v_string) == 0);
 
+	case VAR_PARTIAL:
+	    return tv1->vval.v_partial != NULL
+		    && tv1->vval.v_partial == tv2->vval.v_partial;
+
 	case VAR_NUMBER:
 	    return tv1->vval.v_number == tv2->vval.v_number;
 
@@ -7793,6 +7811,12 @@ echo_string(
 	    r = tv->vval.v_string;
 	    break;
 
+	case VAR_PARTIAL:
+	    *tofree = NULL;
+	    /* TODO: arguments */
+	    r = tv->vval.v_partial == NULL ? NULL : tv->vval.v_partial->pt_name;
+	    break;
+
 	case VAR_LIST:
 	    if (tv->vval.v_list == NULL)
 	    {
@@ -7878,6 +7902,10 @@ tv2string(
 	case VAR_FUNC:
 	    *tofree = string_quote(tv->vval.v_string, TRUE);
 	    return *tofree;
+	case VAR_PARTIAL:
+	    *tofree = string_quote(tv->vval.v_partial == NULL ? NULL
+					 : tv->vval.v_partial->pt_name, TRUE);
+	    return *tofree;
 	case VAR_STRING:
 	    *tofree = string_quote(tv->vval.v_string, FALSE);
 	    return *tofree;
@@ -8146,7 +8174,7 @@ static struct fst
     {"foldtext",	0, 0, f_foldtext},
     {"foldtextresult",	1, 1, f_foldtextresult},
     {"foreground",	0, 0, f_foreground},
-    {"function",	1, 1, f_function},
+    {"function",	1, 3, f_function},
     {"garbagecollect",	0, 1, f_garbagecollect},
     {"get",		2, 3, f_get},
     {"getbufline",	2, 3, f_getbufline},
@@ -8524,13 +8552,16 @@ find_internal_func(
 /*
  * Check if "name" is a variable of type VAR_FUNC.  If so, return the function
  * name it contains, otherwise return "name".
+ * If "name" is of type VAR_PARTIAL also return "partial"
  */
     static char_u *
-deref_func_name(char_u *name, int *lenp, int no_autoload)
+deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload)
 {
     dictitem_T	*v;
     int		cc;
 
+    *partial = NULL;
+
     cc = name[*lenp];
     name[*lenp] = NUL;
     v = find_var(name, NULL, no_autoload);
@@ -8546,6 +8577,18 @@ deref_func_name(char_u *name, int *lenp,
 	return v->di_tv.vval.v_string;
     }
 
+    if (v != NULL && v->di_tv.v_type == VAR_PARTIAL)
+    {
+	*partial = v->di_tv.vval.v_partial;
+	if (*partial == NULL)
+	{
+	    *lenp = 0;
+	    return (char_u *)"";	/* just in case */
+	}
+	*lenp = (int)STRLEN((*partial)->pt_name);
+	return (*partial)->pt_name;
+    }
+
     return name;
 }
 
@@ -8563,6 +8606,7 @@ get_func_tv(
     linenr_T	lastline,	/* last line of range */
     int		*doesrange,	/* return: function handled range */
     int		evaluate,
+    partial_T	*partial,	/* for extra arguments */
     dict_T	*selfdict)	/* Dictionary for "self" */
 {
     char_u	*argp;
@@ -8574,7 +8618,7 @@ get_func_tv(
      * Get the arguments.
      */
     argp = *arg;
-    while (argcount < MAX_FUNC_ARGS)
+    while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc))
     {
 	argp = skipwhite(argp + 1);	    /* skip the '(' or ',' */
 	if (*argp == ')' || *argp == ',' || *argp == NUL)
@@ -8595,7 +8639,7 @@ get_func_tv(
 
     if (ret == OK)
 	ret = call_func(name, len, rettv, argcount, argvars,
-			  firstline, lastline, doesrange, evaluate, selfdict);
+		 firstline, lastline, doesrange, evaluate, partial, selfdict);
     else if (!aborting())
     {
 	if (argcount == MAX_FUNC_ARGS)
@@ -8622,14 +8666,15 @@ call_func(
     char_u	*funcname,	/* name of the function */
     int		len,		/* length of "name" */
     typval_T	*rettv,		/* return value goes here */
-    int		argcount,	/* number of "argvars" */
-    typval_T	*argvars,	/* vars for arguments, must have "argcount"
+    int		argcount_in,	/* number of "argvars" */
+    typval_T	*argvars_in,	/* vars for arguments, must have "argcount"
 				   PLUS ONE elements! */
     linenr_T	firstline,	/* first line of range */
     linenr_T	lastline,	/* last line of range */
     int		*doesrange,	/* return: function handled range */
     int		evaluate,
-    dict_T	*selfdict)	/* Dictionary for "self" */
+    partial_T	*partial,	/* optional, can be NULL */
+    dict_T	*selfdict_in)	/* Dictionary for "self" */
 {
     int		ret = FAIL;
 #define ERROR_UNKNOWN	0
@@ -8639,6 +8684,7 @@ call_func(
 #define ERROR_DICT	4
 #define ERROR_NONE	5
 #define ERROR_OTHER	6
+#define ERROR_BOTH	7
     int		error = ERROR_NONE;
     int		i;
     int		llen;
@@ -8647,6 +8693,11 @@ call_func(
     char_u	fname_buf[FLEN_FIXED + 1];
     char_u	*fname;
     char_u	*name;
+    int		argcount = argcount_in;
+    typval_T	*argvars = argvars_in;
+    dict_T	*selfdict = selfdict_in;
+    typval_T	argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */
+    int		argv_clear = 0;
 
     /* Make a copy of the name, if it comes from a funcref variable it could
      * be changed or deleted in the called function. */
@@ -8698,6 +8749,27 @@ call_func(
 
     *doesrange = FALSE;
 
+    if (partial != NULL)
+    {
+	if (partial->pt_dict != NULL)
+	{
+	    if (selfdict_in != NULL)
+		error = ERROR_BOTH;
+	    selfdict = partial->pt_dict;
+	}
+	if (error == ERROR_NONE && partial->pt_argc > 0)
+	{
+	    int	    i;
+
+	    for (argv_clear = 0; argv_clear < partial->pt_argc; ++argv_clear)
+		copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]);
+	    for (i = 0; i < argcount_in; ++i)
+		argv[i + argv_clear] = argvars_in[i];
+	    argvars = argv;
+	    argcount = partial->pt_argc + argcount_in;
+	}
+    }
+
 
     /* execute the function if no errors detected and executing */
     if (evaluate && error == ERROR_NONE)
@@ -8841,9 +8913,15 @@ call_func(
 		    emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
 									name);
 		    break;
-	}
-    }
-
+	    case ERROR_BOTH:
+		    emsg_funcname(N_("E924: can't have both a \"self\" dict and a partial: %s"),
+									name);
+		    break;
+	}
+    }
+
+    while (argv_clear > 0)
+	clear_tv(&argv[--argv_clear]);
     if (fname != name && fname != fname_buf)
 	vim_free(fname);
     vim_free(name);
@@ -9737,6 +9815,7 @@ f_byteidxcomp(typval_T *argvars, typval_
 func_call(
     char_u	*name,
     typval_T	*args,
+    partial_T	*partial,
     dict_T	*selfdict,
     typval_T	*rettv)
 {
@@ -9749,7 +9828,7 @@ func_call(
     for (item = args->vval.v_list->lv_first; item != NULL;
 							 item = item->li_next)
     {
-	if (argc == MAX_FUNC_ARGS)
+	if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc))
 	{
 	    EMSG(_("E699: Too many arguments"));
 	    break;
@@ -9763,7 +9842,7 @@ func_call(
     if (item == NULL)
 	r = call_func(name, (int)STRLEN(name), rettv, argc, argv,
 				 curwin->w_cursor.lnum, curwin->w_cursor.lnum,
-						      &dummy, TRUE, selfdict);
+					     &dummy, TRUE, partial, selfdict);
 
     /* Free the arguments. */
     while (argc > 0)
@@ -9773,12 +9852,13 @@ func_call(
 }
 
 /*
- * "call(func, arglist)" function
+ * "call(func, arglist [, dict])" function
  */
     static void
 f_call(typval_T *argvars, typval_T *rettv)
 {
     char_u	*func;
+    partial_T   *partial = NULL;
     dict_T	*selfdict = NULL;
 
     if (argvars[1].v_type != VAR_LIST)
@@ -9791,6 +9871,11 @@ f_call(typval_T *argvars, typval_T *rett
 
     if (argvars[0].v_type == VAR_FUNC)
 	func = argvars[0].vval.v_string;
+    else if (argvars[0].v_type == VAR_PARTIAL)
+    {
+	partial = argvars[0].vval.v_partial;
+	func = partial->pt_name;
+    }
     else
 	func = get_tv_string(&argvars[0]);
     if (*func == NUL)
@@ -9806,7 +9891,7 @@ f_call(typval_T *argvars, typval_T *rett
 	selfdict = argvars[2].vval.v_dict;
     }
 
-    (void)func_call(func, &argvars[1], selfdict, rettv);
+    (void)func_call(func, &argvars[1], partial, selfdict, rettv);
 }
 
 #ifdef FEAT_FLOAT
@@ -10627,6 +10712,9 @@ f_empty(typval_T *argvars, typval_T *ret
 	    n = argvars[0].vval.v_string == NULL
 					  || *argvars[0].vval.v_string == NUL;
 	    break;
+	case VAR_PARTIAL:
+	    n = FALSE;
+	    break;
 	case VAR_NUMBER:
 	    n = argvars[0].vval.v_number == 0;
 	    break;
@@ -11688,6 +11776,7 @@ f_foreground(typval_T *argvars UNUSED, t
 f_function(typval_T *argvars, typval_T *rettv)
 {
     char_u	*s;
+    char_u	*name;
 
     s = get_tv_string(&argvars[0]);
     if (s == NULL || *s == NUL || VIM_ISDIGIT(*s))
@@ -11707,18 +11796,118 @@ f_function(typval_T *argvars, typval_T *
 	     * would also work, but some plugins depend on the name being
 	     * printable text. */
 	    sprintf(sid_buf, "<SNR>%ld_", (long)current_SID);
-	    rettv->vval.v_string =
-			    alloc((int)(STRLEN(sid_buf) + STRLEN(s + off) + 1));
-	    if (rettv->vval.v_string != NULL)
-	    {
-		STRCPY(rettv->vval.v_string, sid_buf);
-		STRCAT(rettv->vval.v_string, s + off);
-	    }
-	}
-	else
-	    rettv->vval.v_string = vim_strsave(s);
-	rettv->v_type = VAR_FUNC;
-    }
+	    name = alloc((int)(STRLEN(sid_buf) + STRLEN(s + off) + 1));
+	    if (name != NULL)
+	    {
+		STRCPY(name, sid_buf);
+		STRCAT(name, s + off);
+	    }
+	}
+	else
+	    name = vim_strsave(s);
+
+	if (argvars[1].v_type != VAR_UNKNOWN)
+	{
+	    partial_T	*pt;
+	    int		dict_idx = 0;
+	    int		arg_idx = 0;
+
+	    if (argvars[2].v_type != VAR_UNKNOWN)
+	    {
+		/* function(name, [args], dict) */
+		arg_idx = 1;
+		dict_idx = 2;
+	    }
+	    else if (argvars[1].v_type == VAR_DICT)
+		/* function(name, dict) */
+		dict_idx = 1;
+	    else
+		/* function(name, [args]) */
+		arg_idx = 1;
+	    if (dict_idx > 0 && (argvars[dict_idx].v_type != VAR_DICT
+				     || argvars[dict_idx].vval.v_dict == NULL))
+	    {
+		EMSG(_("E922: expected a dict"));
+		vim_free(name);
+		return;
+	    }
+	    if (arg_idx > 0 && (argvars[arg_idx].v_type != VAR_LIST
+				     || argvars[arg_idx].vval.v_list == NULL))
+	    {
+		EMSG(_("E923: Second argument of function() must be a list or a dict"));
+		vim_free(name);
+		return;
+	    }
+
+	    pt = (partial_T *)alloc_clear(sizeof(partial_T));
+	    if (pt != NULL)
+	    {
+		if (arg_idx > 0)
+		{
+		    list_T	*list = argvars[arg_idx].vval.v_list;
+		    listitem_T	*li;
+		    int		i = 0;
+
+		    pt->pt_argv = (typval_T *)alloc(
+					     sizeof(typval_T) * list->lv_len);
+		    if (pt->pt_argv == NULL)
+		    {
+			vim_free(pt);
+			vim_free(name);
+			return;
+		    }
+		    else
+		    {
+			pt->pt_argc = list->lv_len;
+			for (li = list->lv_first; li != NULL; li = li->li_next)
+			    copy_tv(&li->li_tv, &pt->pt_argv[i++]);
+		    }
+		}
+
+		if (dict_idx > 0)
+		{
+		    pt->pt_dict = argvars[dict_idx].vval.v_dict;
+		    ++pt->pt_dict->dv_refcount;
+		}
+
+		pt->pt_refcount = 1;
+		pt->pt_name = name;
+		func_ref(pt->pt_name);
+	    }
+	    rettv->v_type = VAR_PARTIAL;
+	    rettv->vval.v_partial = pt;
+	}
+	else
+	{
+	    rettv->v_type = VAR_FUNC;
+	    rettv->vval.v_string = name;
+	    func_ref(name);
+	}
+    }
+}
+
+    static void
+partial_free(partial_T *pt)
+{
+    int i;
+
+    for (i = 0; i < pt->pt_argc; ++i)
+	clear_tv(&pt->pt_argv[i]);
+    vim_free(pt->pt_argv);
+    func_unref(pt->pt_name);
+    vim_free(pt->pt_name);
+    vim_free(pt);
+}
+
+/*
+ * Unreference a closure: decrement the reference count and free it when it
+ * becomes zero.
+ */
+    void
+partial_unref(partial_T *pt)
+{
+    if (pt != NULL && --pt->pt_refcount <= 0)
+	partial_free(pt);
 }
 
 /*
@@ -14598,6 +14787,7 @@ f_len(typval_T *argvars, typval_T *rettv
 	case VAR_SPECIAL:
 	case VAR_FLOAT:
 	case VAR_FUNC:
+	case VAR_PARTIAL:
 	case VAR_JOB:
 	case VAR_CHANNEL:
 	    EMSG(_("E701: Invalid type for len()"));
@@ -18169,6 +18359,7 @@ typedef struct
     int		item_compare_float;
 #endif
     char_u	*item_compare_func;
+    partial_T	*item_compare_partial;
     dict_T	*item_compare_selfdict;
     int		item_compare_func_err;
     int		item_compare_keep_zero;
@@ -18278,6 +18469,8 @@ item_compare2(const void *s1, const void
     typval_T	rettv;
     typval_T	argv[3];
     int		dummy;
+    char_u	*func_name;
+    partial_T	*partial = sortinfo->item_compare_partial;
 
     /* shortcut after failure in previous call; compare all items equal */
     if (sortinfo->item_compare_func_err)
@@ -18286,16 +18479,20 @@ item_compare2(const void *s1, const void
     si1 = (sortItem_T *)s1;
     si2 = (sortItem_T *)s2;
 
+    if (partial == NULL)
+	func_name = sortinfo->item_compare_func;
+    else
+	func_name = partial->pt_name;
+
     /* Copy the values.  This is needed to be able to set v_lock to VAR_FIXED
      * in the copy without changing the original list items. */
     copy_tv(&si1->item->li_tv, &argv[0]);
     copy_tv(&si2->item->li_tv, &argv[1]);
 
     rettv.v_type = VAR_UNKNOWN;		/* clear_tv() uses this */
-    res = call_func(sortinfo->item_compare_func,
-				 (int)STRLEN(sortinfo->item_compare_func),
+    res = call_func(func_name, (int)STRLEN(func_name),
 				 &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
-				 sortinfo->item_compare_selfdict);
+				 partial, sortinfo->item_compare_selfdict);
     clear_tv(&argv[0]);
     clear_tv(&argv[1]);
 
@@ -18358,12 +18555,15 @@ do_sort_uniq(typval_T *argvars, typval_T
 	info.item_compare_float = FALSE;
 #endif
 	info.item_compare_func = NULL;
+	info.item_compare_partial = NULL;
 	info.item_compare_selfdict = NULL;
 	if (argvars[1].v_type != VAR_UNKNOWN)
 	{
 	    /* optional second argument: {func} */
 	    if (argvars[1].v_type == VAR_FUNC)
 		info.item_compare_func = argvars[1].vval.v_string;
+	    else if (argvars[1].v_type == VAR_PARTIAL)
+		info.item_compare_partial = argvars[1].vval.v_partial;
 	    else
 	    {
 		int	    error = FALSE;
@@ -18443,7 +18643,8 @@ do_sort_uniq(typval_T *argvars, typval_T
 	    info.item_compare_func_err = FALSE;
 	    info.item_compare_keep_zero = FALSE;
 	    /* test the compare function */
-	    if (info.item_compare_func != NULL
+	    if ((info.item_compare_func != NULL
+					 || info.item_compare_partial != NULL)
 		    && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
 							 == ITEM_COMPARE_FAIL)
 		EMSG(_("E702: Sort compare function failed"));
@@ -18452,6 +18653,7 @@ do_sort_uniq(typval_T *argvars, typval_T
 		/* Sort the array with item pointers. */
 		qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
 		    info.item_compare_func == NULL
+					  && info.item_compare_partial == NULL
 					       ? item_compare : item_compare2);
 
 		if (!info.item_compare_func_err)
@@ -18471,7 +18673,8 @@ do_sort_uniq(typval_T *argvars, typval_T
 	    /* f_uniq(): ptrs will be a stack of items to remove */
 	    info.item_compare_func_err = FALSE;
 	    info.item_compare_keep_zero = TRUE;
-	    item_compare_func_ptr = info.item_compare_func
+	    item_compare_func_ptr = info.item_compare_func != NULL
+					  || info.item_compare_partial != NULL
 					       ? item_compare2 : item_compare;
 
 	    for (li = l->lv_first; li != NULL && li->li_next != NULL;
@@ -20045,6 +20248,7 @@ f_type(typval_T *argvars, typval_T *rett
 	     break;
 	case VAR_JOB:     n = 8; break;
 	case VAR_CHANNEL: n = 9; break;
+	case VAR_PARTIAL: n = 10; break;
 	case VAR_UNKNOWN:
 	     EMSG2(_(e_intern2), "f_type(UNKNOWN)");
 	     n = -1;
@@ -21295,11 +21499,13 @@ handle_subscript(
     char_u	*s;
     int		len;
     typval_T	functv;
+    partial_T	*pt = NULL;
 
     while (ret == OK
 	    && (**arg == '['
 		|| (**arg == '.' && rettv->v_type == VAR_DICT)
-		|| (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC)))
+		|| (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
+					    || rettv->v_type == VAR_PARTIAL)))
 	    && !vim_iswhite(*(*arg - 1)))
     {
 	if (**arg == '(')
@@ -21311,13 +21517,19 @@ handle_subscript(
 		rettv->v_type = VAR_UNKNOWN;
 
 		/* Invoke the function.  Recursive! */
-		s = functv.vval.v_string;
+		if (rettv->v_type == VAR_PARTIAL)
+		{
+		    pt = functv.vval.v_partial;
+		    s = pt->pt_name;
+		}
+		else
+		    s = functv.vval.v_string;
 	    }
 	    else
 		s = (char_u *)"";
 	    ret = get_func_tv(s, (int)STRLEN(s), rettv, arg,
 			curwin->w_cursor.lnum, curwin->w_cursor.lnum,
-			&len, evaluate, selfdict);
+			&len, evaluate, pt, selfdict);
 
 	    /* Clear the funcref afterwards, so that deleting it while
 	     * evaluating the arguments is possible (see test55). */
@@ -21405,6 +21617,9 @@ free_tv(typval_T *varp)
 	    case VAR_STRING:
 		vim_free(varp->vval.v_string);
 		break;
+	    case VAR_PARTIAL:
+		partial_unref(varp->vval.v_partial);
+		break;
 	    case VAR_LIST:
 		list_unref(varp->vval.v_list);
 		break;
@@ -21448,6 +21663,10 @@ clear_tv(typval_T *varp)
 		vim_free(varp->vval.v_string);
 		varp->vval.v_string = NULL;
 		break;
+	    case VAR_PARTIAL:
+		partial_unref(varp->vval.v_partial);
+		varp->vval.v_partial = NULL;
+		break;
 	    case VAR_LIST:
 		list_unref(varp->vval.v_list);
 		varp->vval.v_list = NULL;
@@ -21524,6 +21743,7 @@ get_tv_number_chk(typval_T *varp, int *d
 	    break;
 #endif
 	case VAR_FUNC:
+	case VAR_PARTIAL:
 	    EMSG(_("E703: Using a Funcref as a Number"));
 	    break;
 	case VAR_STRING:
@@ -21572,6 +21792,7 @@ get_tv_float(typval_T *varp)
 	case VAR_FLOAT:
 	    return varp->vval.v_float;
 	case VAR_FUNC:
+	case VAR_PARTIAL:
 	    EMSG(_("E891: Using a Funcref as a Float"));
 	    break;
 	case VAR_STRING:
@@ -21688,6 +21909,7 @@ get_tv_string_buf_chk(typval_T *varp, ch
 	    sprintf((char *)buf, "%ld", (long)varp->vval.v_number);
 	    return buf;
 	case VAR_FUNC:
+	case VAR_PARTIAL:
 	    EMSG(_("E729: using Funcref as a String"));
 	    break;
 	case VAR_LIST:
@@ -22087,7 +22309,7 @@ list_one_var_a(
     msg_advance(22);
     if (type == VAR_NUMBER)
 	msg_putchar('#');
-    else if (type == VAR_FUNC)
+    else if (type == VAR_FUNC || type == VAR_PARTIAL)
 	msg_putchar('*');
     else if (type == VAR_LIST)
     {
@@ -22106,7 +22328,7 @@ list_one_var_a(
 
     msg_outtrans(string);
 
-    if (type == VAR_FUNC)
+    if (type == VAR_FUNC || type == VAR_PARTIAL)
 	msg_puts((char_u *)"()");
     if (*first)
     {
@@ -22138,7 +22360,8 @@ set_var(
     }
     v = find_var_in_ht(ht, 0, varname, TRUE);
 
-    if (tv->v_type == VAR_FUNC && var_check_func_name(name, v == NULL))
+    if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
+				      && var_check_func_name(name, v == NULL))
 	return;
 
     if (v != NULL)
@@ -22383,6 +22606,15 @@ copy_tv(typval_T *from, typval_T *to)
 		    func_ref(to->vval.v_string);
 	    }
 	    break;
+	case VAR_PARTIAL:
+	    if (from->vval.v_partial == NULL)
+		to->vval.v_partial = NULL;
+	    else
+	    {
+		to->vval.v_partial = from->vval.v_partial;
+		++to->vval.v_partial->pt_refcount;
+	    }
+	    break;
 	case VAR_LIST:
 	    if (from->vval.v_list == NULL)
 		to->vval.v_list = NULL;
@@ -22437,6 +22669,7 @@ item_copy(
 	case VAR_FLOAT:
 	case VAR_STRING:
 	case VAR_FUNC:
+	case VAR_PARTIAL:
 	case VAR_SPECIAL:
 	case VAR_JOB:
 	case VAR_CHANNEL:
@@ -23415,6 +23648,7 @@ trans_function_name(
     char_u	sid_buf[20];
     int		len;
     lval_T	lv;
+    partial_T	*partial;
 
     if (fdp != NULL)
 	vim_memset(fdp, 0, sizeof(funcdict_T));
@@ -23499,14 +23733,15 @@ trans_function_name(
     if (lv.ll_exp_name != NULL)
     {
 	len = (int)STRLEN(lv.ll_exp_name);
-	name = deref_func_name(lv.ll_exp_name, &len, flags & TFN_NO_AUTOLOAD);
+	name = deref_func_name(lv.ll_exp_name, &len, &partial,
+						     flags & TFN_NO_AUTOLOAD);
 	if (name == lv.ll_exp_name)
 	    name = NULL;
     }
     else
     {
 	len = (int)(end - *pp);
-	name = deref_func_name(*pp, &len, flags & TFN_NO_AUTOLOAD);
+	name = deref_func_name(*pp, &len, &partial, flags & TFN_NO_AUTOLOAD);
 	if (name == *pp)
 	    name = NULL;
     }
@@ -25111,6 +25346,7 @@ write_viminfo_varlist(FILE *fp)
 
 		    case VAR_UNKNOWN:
 		    case VAR_FUNC:
+		    case VAR_PARTIAL:
 		    case VAR_JOB:
 		    case VAR_CHANNEL:
 				     continue;