diff src/userfunc.c @ 19181:94eda51ba9ba v8.2.0149

patch 8.2.0149: maintaining a Vim9 branch separately is more work Commit: https://github.com/vim/vim/commit/8a7d6542b33e5d2b352262305c3bfdb2d14e1cf8 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 26 15:56:19 2020 +0100 patch 8.2.0149: maintaining a Vim9 branch separately is more work Problem: Maintaining a Vim9 branch separately is more work. Solution: Merge the Vim9 script changes.
author Bram Moolenaar <Bram@vim.org>
date Sun, 26 Jan 2020 16:00:05 +0100
parents af1eca322b9e
children 1168c53d1b49
line wrap: on
line diff
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -22,8 +22,8 @@
 #define FC_DELETED  0x10	// :delfunction used while uf_refcount > 0
 #define FC_REMOVED  0x20	// function redefined while uf_refcount > 0
 #define FC_SANDBOX  0x40	// function defined in the sandbox
-
-#define FUNCARG(fp, j)	((char_u **)(fp->uf_args.ga_data))[j]
+#define FC_DEAD	    0x80	// function kept only for reference to dfunc
+#define FC_EXPORT   0x100	// "export def Func()"
 
 /*
  * All user-defined functions are found in this hashtable.
@@ -63,13 +63,84 @@ func_tbl_get(void)
 }
 
 /*
+ * Get one function argument and an optional type: "arg: type".
+ * Return a pointer to after the type.
+ * When something is wrong return "arg".
+ */
+    static char_u *
+one_function_arg(char_u *arg, garray_T *newargs, garray_T *argtypes, int skip)
+{
+    char_u *p = arg;
+
+    while (ASCII_ISALNUM(*p) || *p == '_')
+	++p;
+    if (arg == p || isdigit(*arg)
+	    || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
+	    || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0))
+    {
+	if (!skip)
+	    semsg(_("E125: Illegal argument: %s"), arg);
+	return arg;
+    }
+    if (newargs != NULL && ga_grow(newargs, 1) == FAIL)
+	return arg;
+    if (newargs != NULL)
+    {
+	char_u	*arg_copy;
+	int	c;
+	int	i;
+
+	c = *p;
+	*p = NUL;
+	arg_copy = vim_strsave(arg);
+	if (arg_copy == NULL)
+	{
+	    *p = c;
+	    return arg;
+	}
+
+	// Check for duplicate argument name.
+	for (i = 0; i < newargs->ga_len; ++i)
+	    if (STRCMP(((char_u **)(newargs->ga_data))[i], arg_copy) == 0)
+	    {
+		semsg(_("E853: Duplicate argument name: %s"), arg_copy);
+		vim_free(arg_copy);
+		return arg;
+	    }
+	((char_u **)(newargs->ga_data))[newargs->ga_len] = arg_copy;
+	newargs->ga_len++;
+
+	*p = c;
+    }
+
+    // get any type from "arg: type"
+    if (argtypes != NULL && ga_grow(argtypes, 1) == OK)
+    {
+	char_u *type = NULL;
+
+	if (*p == ':')
+	{
+	    type = skipwhite(p + 1);
+	    p = skip_type(type);
+	    type = vim_strnsave(type, p - type);
+	}
+	else if (*skipwhite(p) == ':')
+	    emsg(_("E1059: No white space allowed before :"));
+	((char_u **)argtypes->ga_data)[argtypes->ga_len++] = type;
+    }
+
+    return p;
+}
+
+/*
  * Get function arguments.
  */
-    static int
+    int
 get_function_args(
     char_u	**argp,
     char_u	endchar,
     garray_T	*newargs,
+    garray_T	*argtypes,	// NULL unless using :def
     int		*varargs,
     garray_T	*default_args,
     int		skip)
@@ -78,12 +149,13 @@ get_function_args(
     char_u	*arg = *argp;
     char_u	*p = arg;
     int		c;
-    int		i;
     int		any_default = FALSE;
     char_u	*expr;
 
     if (newargs != NULL)
 	ga_init2(newargs, (int)sizeof(char_u *), 3);
+    if (argtypes != NULL)
+	ga_init2(argtypes, (int)sizeof(char_u *), 3);
     if (default_args != NULL)
 	ga_init2(default_args, (int)sizeof(char_u *), 3);
 
@@ -101,46 +173,29 @@ get_function_args(
 		*varargs = TRUE;
 	    p += 3;
 	    mustend = TRUE;
+
+	    if (argtypes != NULL)
+	    {
+		// ...name: list<type>
+		if (!ASCII_ISALPHA(*p))
+		{
+		    emsg(_("E1055: Missing name after ..."));
+		    break;
+		}
+
+		arg = p;
+		p = one_function_arg(p, newargs, argtypes, skip);
+		if (p == arg)
+		    break;
+	    }
 	}
 	else
 	{
 	    arg = p;
-	    while (ASCII_ISALNUM(*p) || *p == '_')
-		++p;
-	    if (arg == p || isdigit(*arg)
-		    || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
-		    || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0))
-	    {
-		if (!skip)
-		    semsg(_("E125: Illegal argument: %s"), arg);
+	    p = one_function_arg(p, newargs, argtypes, skip);
+	    if (p == arg)
 		break;
-	    }
-	    if (newargs != NULL && ga_grow(newargs, 1) == FAIL)
-		goto err_ret;
-	    if (newargs != NULL)
-	    {
-		c = *p;
-		*p = NUL;
-		arg = vim_strsave(arg);
-		if (arg == NULL)
-		{
-		    *p = c;
-		    goto err_ret;
-		}
-
-		// Check for duplicate argument name.
-		for (i = 0; i < newargs->ga_len; ++i)
-		    if (STRCMP(((char_u **)(newargs->ga_data))[i], arg) == 0)
-		    {
-			semsg(_("E853: Duplicate argument name: %s"), arg);
-			vim_free(arg);
-			goto err_ret;
-		    }
-		((char_u **)(newargs->ga_data))[newargs->ga_len] = arg;
-		newargs->ga_len++;
-
-		*p = c;
-	    }
+
 	    if (*skipwhite(p) == '=' && default_args != NULL)
 	    {
 		typval_T	rettv;
@@ -266,7 +321,7 @@ get_lambda_tv(char_u **arg, typval_T *re
     ga_init(&newlines);
 
     // First, check if this is a lambda expression. "->" must exist.
-    ret = get_function_args(&start, '-', NULL, NULL, NULL, TRUE);
+    ret = get_function_args(&start, '-', NULL, NULL, NULL, NULL, TRUE);
     if (ret == FAIL || *start != '>')
 	return NOTDONE;
 
@@ -276,7 +331,8 @@ get_lambda_tv(char_u **arg, typval_T *re
     else
 	pnewargs = NULL;
     *arg = skipwhite(*arg + 1);
-    ret = get_function_args(arg, '-', pnewargs, &varargs, NULL, FALSE);
+    // TODO: argument types
+    ret = get_function_args(arg, '-', pnewargs, NULL, &varargs, NULL, FALSE);
     if (ret == FAIL || **arg != '>')
 	goto errret;
 
@@ -307,6 +363,7 @@ get_lambda_tv(char_u **arg, typval_T *re
 	fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
 	if (fp == NULL)
 	    goto errret;
+	fp->uf_dfunc_idx = -1;
 	pt = ALLOC_CLEAR_ONE(partial_T);
 	if (pt == NULL)
 	    goto errret;
@@ -345,6 +402,7 @@ get_lambda_tv(char_u **arg, typval_T *re
 #endif
 	if (sandbox)
 	    flags |= FC_SANDBOX;
+	// can be called with more args than uf_args.ga_len
 	fp->uf_varargs = TRUE;
 	fp->uf_flags = flags;
 	fp->uf_calls = 0;
@@ -581,17 +639,73 @@ fname_trans_sid(char_u *name, char_u *fn
 }
 
 /*
+ * Find a function "name" in script "sid".
+ */
+    static ufunc_T *
+find_func_with_sid(char_u *name, int sid)
+{
+    hashitem_T	*hi;
+    char_u	buffer[200];
+
+    buffer[0] = K_SPECIAL;
+    buffer[1] = KS_EXTRA;
+    buffer[2] = (int)KE_SNR;
+    vim_snprintf((char *)buffer + 3, sizeof(buffer) - 3, "%ld_%s",
+							      (long)sid, name);
+    hi = hash_find(&func_hashtab, buffer);
+    if (!HASHITEM_EMPTY(hi))
+	return HI2UF(hi);
+
+    return NULL;
+}
+
+/*
  * Find a function by name, return pointer to it in ufuncs.
  * Return NULL for unknown function.
  */
-    ufunc_T *
-find_func(char_u *name)
+    static ufunc_T *
+find_func_even_dead(char_u *name, cctx_T *cctx)
 {
     hashitem_T	*hi;
+    ufunc_T	*func;
+    imported_T	*imported;
+
+    if (in_vim9script())
+    {
+	// Find script-local function before global one.
+	func = find_func_with_sid(name, current_sctx.sc_sid);
+	if (func != NULL)
+	    return func;
+
+	// Find imported funcion before global one.
+	imported = find_imported(name, cctx);
+	if (imported != NULL && imported->imp_funcname != NULL)
+	{
+	    hi = hash_find(&func_hashtab, imported->imp_funcname);
+	    if (!HASHITEM_EMPTY(hi))
+		return HI2UF(hi);
+	}
+    }
 
     hi = hash_find(&func_hashtab, name);
     if (!HASHITEM_EMPTY(hi))
 	return HI2UF(hi);
+
+    return NULL;
+}
+
+/*
+ * Find a function by name, return pointer to it in ufuncs.
+ * "cctx" is passed in a :def function to find imported functions.
+ * Return NULL for unknown or dead function.
+ */
+    ufunc_T *
+find_func(char_u *name, cctx_T *cctx)
+{
+    ufunc_T	*fp = find_func_even_dead(name, cctx);
+
+    if (fp != NULL && (fp->uf_flags & FC_DEAD) == 0)
+	return fp;
     return NULL;
 }
 
@@ -761,6 +875,127 @@ cleanup_function_call(funccall_T *fc)
 	}
     }
 }
+/*
+ * Unreference "fc": decrement the reference count and free it when it
+ * becomes zero.  "fp" is detached from "fc".
+ * When "force" is TRUE we are exiting.
+ */
+    static void
+funccal_unref(funccall_T *fc, ufunc_T *fp, int force)
+{
+    funccall_T	**pfc;
+    int		i;
+
+    if (fc == NULL)
+	return;
+
+    if (--fc->fc_refcount <= 0 && (force || (
+		fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
+		&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
+		&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)))
+	for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller)
+	{
+	    if (fc == *pfc)
+	    {
+		*pfc = fc->caller;
+		free_funccal_contents(fc);
+		return;
+	    }
+	}
+    for (i = 0; i < fc->fc_funcs.ga_len; ++i)
+	if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp)
+	    ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
+}
+
+/*
+ * Remove the function from the function hashtable.  If the function was
+ * deleted while it still has references this was already done.
+ * Return TRUE if the entry was deleted, FALSE if it wasn't found.
+ */
+    static int
+func_remove(ufunc_T *fp)
+{
+    hashitem_T	*hi;
+
+    // Return if it was already virtually deleted.
+    if (fp->uf_flags & FC_DEAD)
+	return FALSE;
+
+    hi = hash_find(&func_hashtab, UF2HIKEY(fp));
+    if (!HASHITEM_EMPTY(hi))
+    {
+	// When there is a def-function index do not actually remove the
+	// function, so we can find the index when defining the function again.
+	if (fp->uf_dfunc_idx >= 0)
+	    fp->uf_flags |= FC_DEAD;
+	else
+	    hash_remove(&func_hashtab, hi);
+	return TRUE;
+    }
+    return FALSE;
+}
+
+    static void
+func_clear_items(ufunc_T *fp)
+{
+    ga_clear_strings(&(fp->uf_args));
+    ga_clear_strings(&(fp->uf_def_args));
+    ga_clear_strings(&(fp->uf_lines));
+    VIM_CLEAR(fp->uf_name_exp);
+    VIM_CLEAR(fp->uf_arg_types);
+    ga_clear(&fp->uf_type_list);
+#ifdef FEAT_PROFILE
+    VIM_CLEAR(fp->uf_tml_count);
+    VIM_CLEAR(fp->uf_tml_total);
+    VIM_CLEAR(fp->uf_tml_self);
+#endif
+}
+
+/*
+ * Free all things that a function contains.  Does not free the function
+ * itself, use func_free() for that.
+ * When "force" is TRUE we are exiting.
+ */
+    static void
+func_clear(ufunc_T *fp, int force)
+{
+    if (fp->uf_cleared)
+	return;
+    fp->uf_cleared = TRUE;
+
+    // clear this function
+    func_clear_items(fp);
+    funccal_unref(fp->uf_scoped, fp, force);
+    delete_def_function(fp);
+}
+
+/*
+ * Free a function and remove it from the list of functions.  Does not free
+ * what a function contains, call func_clear() first.
+ */
+    static void
+func_free(ufunc_T *fp)
+{
+    // Only remove it when not done already, otherwise we would remove a newer
+    // version of the function with the same name.
+    if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0)
+	func_remove(fp);
+
+    if ((fp->uf_flags & FC_DEAD) == 0)
+	vim_free(fp);
+}
+
+/*
+ * Free all things that a function contains and free the function itself.
+ * When "force" is TRUE we are exiting.
+ */
+    static void
+func_clear_free(ufunc_T *fp, int force)
+{
+    func_clear(fp, force);
+    func_free(fp);
+}
+
 
 /*
  * Call a user function.
@@ -822,6 +1057,20 @@ call_user_func(
     ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1);
     func_ptr_ref(fp);
 
+    if (fp->uf_dfunc_idx >= 0)
+    {
+	estack_push_ufunc(ETYPE_UFUNC, fp, 1);
+
+	// Execute the compiled function.
+	call_def_function(fp, argcount, argvars, rettv);
+	--depth;
+	current_funccal = fc->caller;
+
+	estack_pop();
+	free_funccal(fc);
+	return;
+    }
+
     if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
 	islambda = TRUE;
 
@@ -1146,110 +1395,57 @@ call_user_func(
 }
 
 /*
- * Unreference "fc": decrement the reference count and free it when it
- * becomes zero.  "fp" is detached from "fc".
- * When "force" is TRUE we are exiting.
+ * Call a user function after checking the arguments.
  */
-    static void
-funccal_unref(funccall_T *fc, ufunc_T *fp, int force)
-{
-    funccall_T	**pfc;
-    int		i;
-
-    if (fc == NULL)
-	return;
-
-    if (--fc->fc_refcount <= 0 && (force || (
-		fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
-		&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
-		&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT)))
-	for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller)
-	{
-	    if (fc == *pfc)
-	    {
-		*pfc = fc->caller;
-		free_funccal_contents(fc);
-		return;
-	    }
-	}
-    for (i = 0; i < fc->fc_funcs.ga_len; ++i)
-	if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp)
-	    ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
-}
-
-/*
- * Remove the function from the function hashtable.  If the function was
- * deleted while it still has references this was already done.
- * Return TRUE if the entry was deleted, FALSE if it wasn't found.
- */
-    static int
-func_remove(ufunc_T *fp)
-{
-    hashitem_T	*hi = hash_find(&func_hashtab, UF2HIKEY(fp));
-
-    if (!HASHITEM_EMPTY(hi))
-    {
-	hash_remove(&func_hashtab, hi);
-	return TRUE;
-    }
-    return FALSE;
-}
-
-    static void
-func_clear_items(ufunc_T *fp)
+    int
+call_user_func_check(
+	ufunc_T	    *fp,
+	int	    argcount,
+	typval_T    *argvars,
+	typval_T    *rettv,
+	funcexe_T   *funcexe,
+	dict_T	    *selfdict)
 {
-    ga_clear_strings(&(fp->uf_args));
-    ga_clear_strings(&(fp->uf_def_args));
-    ga_clear_strings(&(fp->uf_lines));
-    VIM_CLEAR(fp->uf_name_exp);
-#ifdef FEAT_PROFILE
-    VIM_CLEAR(fp->uf_tml_count);
-    VIM_CLEAR(fp->uf_tml_total);
-    VIM_CLEAR(fp->uf_tml_self);
-#endif
-}
-
-/*
- * Free all things that a function contains.  Does not free the function
- * itself, use func_free() for that.
- * When "force" is TRUE we are exiting.
- */
-    static void
-func_clear(ufunc_T *fp, int force)
-{
-    if (fp->uf_cleared)
-	return;
-    fp->uf_cleared = TRUE;
-
-    // clear this function
-    func_clear_items(fp);
-    funccal_unref(fp->uf_scoped, fp, force);
-}
-
-/*
- * Free a function and remove it from the list of functions.  Does not free
- * what a function contains, call func_clear() first.
- */
-    static void
-func_free(ufunc_T *fp)
-{
-    // only remove it when not done already, otherwise we would remove a newer
-    // version of the function
-    if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0)
-	func_remove(fp);
-
-    vim_free(fp);
-}
-
-/*
- * Free all things that a function contains and free the function itself.
- * When "force" is TRUE we are exiting.
- */
-    static void
-func_clear_free(ufunc_T *fp, int force)
-{
-    func_clear(fp, force);
-    func_free(fp);
+    int error;
+    int regular_args = fp->uf_args.ga_len;
+
+    if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL)
+	*funcexe->doesrange = TRUE;
+    if (argcount < regular_args - fp->uf_def_args.ga_len)
+	error = FCERR_TOOFEW;
+    else if (!has_varargs(fp) && argcount > regular_args)
+	error = FCERR_TOOMANY;
+    else if ((fp->uf_flags & FC_DICT) && selfdict == NULL)
+	error = FCERR_DICT;
+    else
+    {
+	int		did_save_redo = FALSE;
+	save_redo_T	save_redo;
+
+	/*
+	 * Call the user function.
+	 * Save and restore search patterns, script variables and
+	 * redo buffer.
+	 */
+	save_search_patterns();
+	if (!ins_compl_active())
+	{
+	    saveRedobuff(&save_redo);
+	    did_save_redo = TRUE;
+	}
+	++fp->uf_calls;
+	call_user_func(fp, argcount, argvars, rettv,
+			     funcexe->firstline, funcexe->lastline,
+		      (fp->uf_flags & FC_DICT) ? selfdict : NULL);
+	if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
+	    // Function was unreferenced while being used, free it now.
+	    func_clear_free(fp, FALSE);
+	if (did_save_redo)
+	    restoreRedobuff(&save_redo);
+	restore_search_patterns();
+	error = FCERR_NONE;
+    }
+    return error;
 }
 
 /*
@@ -1327,9 +1523,13 @@ free_all_functions(void)
 	for (hi = func_hashtab.ht_array; todo > 0; ++hi)
 	    if (!HASHITEM_EMPTY(hi))
 	    {
+		// clear the def function index now
+		fp = HI2UF(hi);
+		fp->uf_flags &= ~FC_DEAD;
+		fp->uf_dfunc_idx = -1;
+
 		// Only free functions that are not refcounted, those are
 		// supposed to be freed when no longer referenced.
-		fp = HI2UF(hi);
 		if (func_name_refcount(fp->uf_name))
 		    ++skipped;
 		else
@@ -1371,6 +1571,8 @@ free_all_functions(void)
     }
     if (skipped == 0)
 	hash_clear(&func_hashtab);
+
+    free_def_functions();
 }
 #endif
 
@@ -1379,7 +1581,7 @@ free_all_functions(void)
  * lower case letter and doesn't contain AUTOLOAD_CHAR.
  * "len" is the length of "name", or -1 for NUL terminated.
  */
-    static int
+    int
 builtin_function(char_u *name, int len)
 {
     char_u *p;
@@ -1469,6 +1671,43 @@ call_callback(
 }
 
 /*
+ * Give an error message for the result of a function.
+ * Nothing if "error" is FCERR_NONE.
+ */
+    void
+user_func_error(int error, char_u *name)
+{
+    switch (error)
+    {
+	case FCERR_UNKNOWN:
+		emsg_funcname(e_unknownfunc, name);
+		break;
+	case FCERR_NOTMETHOD:
+		emsg_funcname(
+			N_("E276: Cannot use function as a method: %s"), name);
+		break;
+	case FCERR_DELETED:
+		emsg_funcname(N_(e_func_deleted), name);
+		break;
+	case FCERR_TOOMANY:
+		emsg_funcname((char *)e_toomanyarg, name);
+		break;
+	case FCERR_TOOFEW:
+		emsg_funcname((char *)e_toofewarg, name);
+		break;
+	case FCERR_SCRIPT:
+		emsg_funcname(
+		    N_("E120: Using <SID> not in a script context: %s"), name);
+		break;
+	case FCERR_DICT:
+		emsg_funcname(
+		      N_("E725: Calling dict function without Dictionary: %s"),
+			name);
+		break;
+    }
+}
+
+/*
  * Call a function with its resolved parameters
  *
  * Return FAIL when the function can't be called,  OK otherwise.
@@ -1561,7 +1800,7 @@ call_func(
 	    if (partial != NULL && partial->pt_func != NULL)
 		fp = partial->pt_func;
 	    else
-		fp = find_func(rfname);
+		fp = find_func(rfname, NULL);
 
 	    // Trigger FuncUndefined event, may load the function.
 	    if (fp == NULL
@@ -1570,13 +1809,13 @@ call_func(
 		    && !aborting())
 	    {
 		// executed an autocommand, search for the function again
-		fp = find_func(rfname);
+		fp = find_func(rfname, NULL);
 	    }
 	    // Try loading a package.
 	    if (fp == NULL && script_autoload(rfname, TRUE) && !aborting())
 	    {
 		// loaded a package, search for the function again
-		fp = find_func(rfname);
+		fp = find_func(rfname, NULL);
 	    }
 
 	    if (fp != NULL && (fp->uf_flags & FC_DELETED))
@@ -1598,43 +1837,8 @@ call_func(
 		    argv_base = 1;
 		}
 
-		if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL)
-		    *funcexe->doesrange = TRUE;
-		if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len)
-		    error = FCERR_TOOFEW;
-		else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len)
-		    error = FCERR_TOOMANY;
-		else if ((fp->uf_flags & FC_DICT) && selfdict == NULL)
-		    error = FCERR_DICT;
-		else
-		{
-		    int did_save_redo = FALSE;
-		    save_redo_T	save_redo;
-
-		    /*
-		     * Call the user function.
-		     * Save and restore search patterns, script variables and
-		     * redo buffer.
-		     */
-		    save_search_patterns();
-		    if (!ins_compl_active())
-		    {
-			saveRedobuff(&save_redo);
-			did_save_redo = TRUE;
-		    }
-		    ++fp->uf_calls;
-		    call_user_func(fp, argcount, argvars, rettv,
-					 funcexe->firstline, funcexe->lastline,
-				  (fp->uf_flags & FC_DICT) ? selfdict : NULL);
-		    if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
-			// Function was unreferenced while being used, free it
-			// now.
-			func_clear_free(fp, FALSE);
-		    if (did_save_redo)
-			restoreRedobuff(&save_redo);
-		    restore_search_patterns();
-		    error = FCERR_NONE;
-		}
+		error = call_user_func_check(fp, argcount, argvars, rettv,
+							    funcexe, selfdict);
 	    }
 	}
 	else if (funcexe->basetv != NULL)
@@ -1675,38 +1879,7 @@ theend:
      */
     if (!aborting())
     {
-	switch (error)
-	{
-	    case FCERR_UNKNOWN:
-		    emsg_funcname(N_("E117: Unknown function: %s"), name);
-		    break;
-	    case FCERR_NOTMETHOD:
-		    emsg_funcname(
-			       N_("E276: Cannot use function as a method: %s"),
-									 name);
-		    break;
-	    case FCERR_DELETED:
-		    emsg_funcname(N_("E933: Function was deleted: %s"), name);
-		    break;
-	    case FCERR_TOOMANY:
-		    emsg_funcname((char *)e_toomanyarg, name);
-		    break;
-	    case FCERR_TOOFEW:
-		    emsg_funcname(
-			     N_("E119: Not enough arguments for function: %s"),
-									name);
-		    break;
-	    case FCERR_SCRIPT:
-		    emsg_funcname(
-			   N_("E120: Using <SID> not in a script context: %s"),
-									name);
-		    break;
-	    case FCERR_DICT:
-		    emsg_funcname(
-		      N_("E725: Calling dict function without Dictionary: %s"),
-									name);
-		    break;
-	}
+	user_func_error(error, name);
     }
 
     // clear the copies made from the partial
@@ -1719,6 +1892,12 @@ theend:
     return ret;
 }
 
+    static char_u *
+printable_func_name(ufunc_T *fp)
+{
+    return fp->uf_name_exp != NULL ? fp->uf_name_exp : fp->uf_name;
+}
+
 /*
  * List the head of the function: "name(arg1, arg2)".
  */
@@ -1731,16 +1910,21 @@ list_func_head(ufunc_T *fp, int indent)
     if (indent)
 	msg_puts("   ");
     msg_puts("function ");
-    if (fp->uf_name_exp != NULL)
-	msg_puts((char *)fp->uf_name_exp);
-    else
-	msg_puts((char *)fp->uf_name);
+    msg_puts((char *)printable_func_name(fp));
     msg_putchar('(');
     for (j = 0; j < fp->uf_args.ga_len; ++j)
     {
 	if (j)
 	    msg_puts(", ");
 	msg_puts((char *)FUNCARG(fp, j));
+	if (fp->uf_arg_types != NULL)
+	{
+	    char *tofree;
+
+	    msg_puts(": ");
+	    msg_puts(type_name(fp->uf_arg_types[j], &tofree));
+	    vim_free(tofree);
+	}
 	if (j >= fp->uf_args.ga_len - fp->uf_def_args.ga_len)
 	{
 	    msg_puts(" = ");
@@ -1754,6 +1938,21 @@ list_func_head(ufunc_T *fp, int indent)
 	    msg_puts(", ");
 	msg_puts("...");
     }
+    if (fp->uf_va_name != NULL)
+    {
+	if (j)
+	    msg_puts(", ");
+	msg_puts("...");
+	msg_puts((char *)fp->uf_va_name);
+	if (fp->uf_va_type)
+	{
+	    char *tofree;
+
+	    msg_puts(": ");
+	    msg_puts(type_name(fp->uf_va_type, &tofree));
+	    vim_free(tofree);
+	}
+    }
     msg_putchar(')');
     if (fp->uf_flags & FC_ABORT)
 	msg_puts(" abort");
@@ -1793,7 +1992,9 @@ trans_function_name(
     int		lead;
     char_u	sid_buf[20];
     int		len;
+    int		extra = 0;
     lval_T	lv;
+    int		vim9script;
 
     if (fdp != NULL)
 	vim_memset(fdp, 0, sizeof(funcdict_T));
@@ -1934,6 +2135,10 @@ trans_function_name(
 	len = (int)(end - lv.ll_name);
     }
 
+    // In Vim9 script a user function is script-local by default.
+    vim9script = ASCII_ISUPPER(*start)
+			     && current_sctx.sc_version == SCRIPT_VERSION_VIM9;
+
     /*
      * Copy the function name to allocated memory.
      * Accept <SID>name() inside a script, translate into <SNR>123_name().
@@ -1941,20 +2146,25 @@ trans_function_name(
      */
     if (skip)
 	lead = 0;	// do nothing
-    else if (lead > 0)
+    else if (lead > 0 || vim9script)
     {
-	lead = 3;
-	if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name))
+	if (!vim9script)
+	    lead = 3;
+	if (vim9script || (lv.ll_exp_name != NULL
+					     && eval_fname_sid(lv.ll_exp_name))
 						       || eval_fname_sid(*pp))
 	{
-	    // It's "s:" or "<SID>"
+	    // It's script-local, "s:" or "<SID>"
 	    if (current_sctx.sc_sid <= 0)
 	    {
 		emsg(_(e_usingsid));
 		goto theend;
 	    }
 	    sprintf((char *)sid_buf, "%ld_", (long)current_sctx.sc_sid);
-	    lead += (int)STRLEN(sid_buf);
+	    if (vim9script)
+		extra = 3 + (int)STRLEN(sid_buf);
+	    else
+		lead += (int)STRLEN(sid_buf);
 	}
     }
     else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, len))
@@ -1974,19 +2184,19 @@ trans_function_name(
 	}
     }
 
-    name = alloc(len + lead + 1);
+    name = alloc(len + lead + extra + 1);
     if (name != NULL)
     {
-	if (lead > 0)
+	if (lead > 0 || vim9script)
 	{
 	    name[0] = K_SPECIAL;
 	    name[1] = KS_EXTRA;
 	    name[2] = (int)KE_SNR;
-	    if (lead > 3)	// If it's "<SID>"
+	    if (vim9script || lead > 3)	// If it's "<SID>"
 		STRCPY(name + 3, sid_buf);
 	}
-	mch_memmove(name + lead, lv.ll_name, (size_t)len);
-	name[lead + len] = NUL;
+	mch_memmove(name + lead + extra, lv.ll_name, (size_t)len);
+	name[lead + extra + len] = NUL;
     }
     *pp = end;
 
@@ -2012,19 +2222,22 @@ ex_function(exarg_T *eap)
     char_u	*arg;
     char_u	*line_arg = NULL;
     garray_T	newargs;
+    garray_T	argtypes;
     garray_T	default_args;
     garray_T	newlines;
     int		varargs = FALSE;
     int		flags = 0;
+    char_u	*ret_type = NULL;
     ufunc_T	*fp;
     int		overwrite = FALSE;
     int		indent;
     int		nesting;
+#define MAX_FUNC_NESTING 50
+    char	nesting_def[MAX_FUNC_NESTING];
     dictitem_T	*v;
     funcdict_T	fudi;
     static int	func_nr = 0;	    // number for nameless function
     int		paren;
-    hashtab_T	*ht;
     int		todo;
     hashitem_T	*hi;
     int		do_concat = TRUE;
@@ -2048,7 +2261,8 @@ ex_function(exarg_T *eap)
 		{
 		    --todo;
 		    fp = HI2UF(hi);
-		    if (message_filtered(fp->uf_name))
+		    if ((fp->uf_flags & FC_DEAD)
+					      || message_filtered(fp->uf_name))
 			continue;
 		    if (!func_name_refcount(fp->uf_name))
 			list_func_head(fp, FALSE);
@@ -2084,8 +2298,9 @@ ex_function(exarg_T *eap)
 		    {
 			--todo;
 			fp = HI2UF(hi);
-			if (!isdigit(*fp->uf_name)
-				    && vim_regexec(&regmatch, fp->uf_name, 0))
+			if ((fp->uf_flags & FC_DEAD) == 0
+				&& !isdigit(*fp->uf_name)
+				&& vim_regexec(&regmatch, fp->uf_name, 0))
 			    list_func_head(fp, FALSE);
 		    }
 		}
@@ -2098,6 +2313,10 @@ ex_function(exarg_T *eap)
 	return;
     }
 
+    ga_init(&newargs);
+    ga_init(&argtypes);
+    ga_init(&default_args);
+
     /*
      * Get the function name.  There are these situations:
      * func	    normal function name
@@ -2155,7 +2374,7 @@ ex_function(exarg_T *eap)
 	    *p = NUL;
 	if (!eap->skip && !got_int)
 	{
-	    fp = find_func(name);
+	    fp = find_func(name, NULL);
 	    if (fp != NULL)
 	    {
 		list_func_head(fp, TRUE);
@@ -2176,7 +2395,10 @@ ex_function(exarg_T *eap)
 		if (!got_int)
 		{
 		    msg_putchar('\n');
-		    msg_puts("   endfunction");
+		    if (fp->uf_dfunc_idx >= 0)
+			msg_puts("   enddef");
+		    else
+			msg_puts("   endfunction");
 		}
 	    }
 	    else
@@ -2231,43 +2453,58 @@ ex_function(exarg_T *eap)
 	    emsg(_("E862: Cannot use g: here"));
     }
 
-    if (get_function_args(&p, ')', &newargs, &varargs,
-					    &default_args, eap->skip) == FAIL)
+    if (get_function_args(&p, ')', &newargs,
+			eap->cmdidx == CMD_def ? &argtypes : NULL,
+			 &varargs, &default_args, eap->skip) == FAIL)
 	goto errret_2;
 
-    // find extra arguments "range", "dict", "abort" and "closure"
-    for (;;)
+    if (eap->cmdidx == CMD_def)
     {
-	p = skipwhite(p);
-	if (STRNCMP(p, "range", 5) == 0)
+	// find the return type: :def Func(): type
+	if (*p == ':')
 	{
-	    flags |= FC_RANGE;
-	    p += 5;
-	}
-	else if (STRNCMP(p, "dict", 4) == 0)
-	{
-	    flags |= FC_DICT;
-	    p += 4;
+	    ret_type = skipwhite(p + 1);
+	    p = skip_type(ret_type);
+	    if (p > ret_type)
+		p = skipwhite(p);
+	    else
+		semsg(_("E1056: expected a type: %s"), ret_type);
 	}
-	else if (STRNCMP(p, "abort", 5) == 0)
-	{
-	    flags |= FC_ABORT;
-	    p += 5;
-	}
-	else if (STRNCMP(p, "closure", 7) == 0)
+    }
+    else
+	// find extra arguments "range", "dict", "abort" and "closure"
+	for (;;)
 	{
-	    flags |= FC_CLOSURE;
-	    p += 7;
-	    if (current_funccal == NULL)
+	    p = skipwhite(p);
+	    if (STRNCMP(p, "range", 5) == 0)
+	    {
+		flags |= FC_RANGE;
+		p += 5;
+	    }
+	    else if (STRNCMP(p, "dict", 4) == 0)
+	    {
+		flags |= FC_DICT;
+		p += 4;
+	    }
+	    else if (STRNCMP(p, "abort", 5) == 0)
 	    {
-		emsg_funcname(N_("E932: Closure function should not be at top level: %s"),
-			name == NULL ? (char_u *)"" : name);
-		goto erret;
+		flags |= FC_ABORT;
+		p += 5;
 	    }
+	    else if (STRNCMP(p, "closure", 7) == 0)
+	    {
+		flags |= FC_CLOSURE;
+		p += 7;
+		if (current_funccal == NULL)
+		{
+		    emsg_funcname(N_("E932: Closure function should not be at top level: %s"),
+			    name == NULL ? (char_u *)"" : name);
+		    goto erret;
+		}
+	    }
+	    else
+		break;
 	}
-	else
-	    break;
-    }
 
     // When there is a line break use what follows for the function body.
     // Makes 'exe "func Test()\n...\nendfunc"' work.
@@ -2277,7 +2514,8 @@ ex_function(exarg_T *eap)
 	emsg(_(e_trailing));
 
     /*
-     * Read the body of the function, until ":endfunction" is found.
+     * Read the body of the function, until "}", ":endfunction" or ":enddef" is
+     * found.
      */
     if (KeyTyped)
     {
@@ -2288,7 +2526,7 @@ ex_function(exarg_T *eap)
 	{
 	    if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL)
 		emsg(_(e_funcdict));
-	    else if (name != NULL && find_func(name) != NULL)
+	    else if (name != NULL && find_func(name, NULL) != NULL)
 		emsg_funcname(e_funcexts, name);
 	}
 
@@ -2304,6 +2542,7 @@ ex_function(exarg_T *eap)
 
     indent = 2;
     nesting = 0;
+    nesting_def[nesting] = (eap->cmdidx == CMD_def);
     for (;;)
     {
 	if (KeyTyped)
@@ -2339,7 +2578,10 @@ ex_function(exarg_T *eap)
 	    lines_left = Rows - 1;
 	if (theline == NULL)
 	{
-	    emsg(_("E126: Missing :endfunction"));
+	    if (eap->cmdidx == CMD_def)
+		emsg(_("E1057: Missing :enddef"));
+	    else
+		emsg(_("E126: Missing :endfunction"));
 	    goto erret;
 	}
 
@@ -2352,7 +2594,7 @@ ex_function(exarg_T *eap)
 
 	if (skip_until != NULL)
 	{
-	    // Don't check for ":endfunc" between
+	    // Don't check for ":endfunc"/":enddef" between
 	    // * ":append" and "."
 	    // * ":python <<EOF" and "EOF"
 	    // * ":let {var-name} =<< [trim] {marker}" and "{marker}"
@@ -2383,8 +2625,9 @@ ex_function(exarg_T *eap)
 	    for (p = theline; VIM_ISWHITE(*p) || *p == ':'; ++p)
 		;
 
-	    // Check for "endfunction".
-	    if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0)
+	    // Check for "endfunction" or "enddef".
+	    if (checkforcmd(&p, nesting_def[nesting]
+			     ? "enddef" : "endfunction", 4) && nesting-- == 0)
 	    {
 		char_u *nextcmd = NULL;
 
@@ -2393,8 +2636,9 @@ ex_function(exarg_T *eap)
 		else if (line_arg != NULL && *skipwhite(line_arg) != NUL)
 		    nextcmd = line_arg;
 		else if (*p != NUL && *p != '"' && p_verbose > 0)
-		    give_warning2(
-			 (char_u *)_("W22: Text found after :endfunction: %s"),
+		    give_warning2(eap->cmdidx == CMD_def
+			? (char_u *)_("W1001: Text found after :enddef: %s")
+			: (char_u *)_("W22: Text found after :endfunction: %s"),
 			 p, TRUE);
 		if (nextcmd != NULL)
 		{
@@ -2414,7 +2658,7 @@ ex_function(exarg_T *eap)
 
 	    // Increase indent inside "if", "while", "for" and "try", decrease
 	    // at "end".
-	    if (indent > 2 && STRNCMP(p, "end", 3) == 0)
+	    if (indent > 2 && (*p == '}' || STRNCMP(p, "end", 3) == 0))
 		indent -= 2;
 	    else if (STRNCMP(p, "if", 2) == 0
 		    || STRNCMP(p, "wh", 2) == 0
@@ -2423,7 +2667,8 @@ ex_function(exarg_T *eap)
 		indent += 2;
 
 	    // Check for defining a function inside this function.
-	    if (checkforcmd(&p, "function", 2))
+	    c = *p;
+	    if (checkforcmd(&p, "function", 2) || checkforcmd(&p, "def", 3))
 	    {
 		if (*p == '!')
 		    p = skipwhite(p + 1);
@@ -2431,8 +2676,14 @@ ex_function(exarg_T *eap)
 		vim_free(trans_function_name(&p, TRUE, 0, NULL, NULL));
 		if (*skipwhite(p) == '(')
 		{
-		    ++nesting;
-		    indent += 2;
+		    if (nesting == MAX_FUNC_NESTING - 1)
+			emsg(_("E1058: function nesting too deep"));
+		    else
+		    {
+			++nesting;
+			nesting_def[nesting] = (c == 'd');
+			indent += 2;
+		    }
 		}
 	    }
 
@@ -2537,6 +2788,8 @@ ex_function(exarg_T *eap)
      */
     if (fudi.fd_dict == NULL)
     {
+	hashtab_T	*ht;
+
 	v = find_var(name, &ht, FALSE);
 	if (v != NULL && v->di_tv.v_type == VAR_FUNC)
 	{
@@ -2545,12 +2798,14 @@ ex_function(exarg_T *eap)
 	    goto erret;
 	}
 
-	fp = find_func(name);
+	fp = find_func_even_dead(name, NULL);
 	if (fp != NULL)
 	{
+	    int dead = fp->uf_flags & FC_DEAD;
+
 	    // Function can be replaced with "function!" and when sourcing the
 	    // same script again, but only once.
-	    if (!eap->forceit
+	    if (!dead && !eap->forceit
 			&& (fp->uf_script_ctx.sc_sid != current_sctx.sc_sid
 			    || fp->uf_script_ctx.sc_seq == current_sctx.sc_seq))
 	    {
@@ -2582,6 +2837,7 @@ ex_function(exarg_T *eap)
 		fp->uf_name_exp = NULL;
 		func_clear_items(fp);
 		fp->uf_name_exp = exp_name;
+		fp->uf_flags &= ~FC_DEAD;
 #ifdef FEAT_PROFILE
 		fp->uf_profiling = FALSE;
 		fp->uf_prof_initialized = FALSE;
@@ -2651,6 +2907,7 @@ ex_function(exarg_T *eap)
 	fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
 	if (fp == NULL)
 	    goto erret;
+	fp->uf_dfunc_idx = -1;
 
 	if (fudi.fd_dict != NULL)
 	{
@@ -2696,6 +2953,61 @@ ex_function(exarg_T *eap)
     }
     fp->uf_args = newargs;
     fp->uf_def_args = default_args;
+    fp->uf_ret_type = &t_any;
+
+    if (eap->cmdidx == CMD_def)
+    {
+	// parse the argument types
+	ga_init2(&fp->uf_type_list, sizeof(type_T), 5);
+
+	if (argtypes.ga_len > 0)
+	{
+	    // When "varargs" is set the last name/type goes into uf_va_name
+	    // and uf_va_type.
+	    int len = argtypes.ga_len - (varargs ? 1 : 0);
+
+	    fp->uf_arg_types = ALLOC_CLEAR_MULT(type_T *, len);
+	    if (fp->uf_arg_types != NULL)
+	    {
+		int i;
+
+		for (i = 0; i < len; ++ i)
+		{
+		    p = ((char_u **)argtypes.ga_data)[i];
+		    if (p == NULL)
+			// todo: get type from default value
+			fp->uf_arg_types[i] = &t_any;
+		    else
+			fp->uf_arg_types[i] = parse_type(&p, &fp->uf_type_list);
+		}
+	    }
+	    if (varargs)
+	    {
+		// Move the last argument "...name: type" to uf_va_name and
+		// uf_va_type.
+		fp->uf_va_name = ((char_u **)fp->uf_args.ga_data)
+						      [fp->uf_args.ga_len - 1];
+		--fp->uf_args.ga_len;
+		p = ((char_u **)argtypes.ga_data)[len];
+		if (p == NULL)
+		    // todo: get type from default value
+		    fp->uf_va_type = &t_any;
+		else
+		    fp->uf_va_type = parse_type(&p, &fp->uf_type_list);
+	    }
+	    varargs = FALSE;
+	}
+
+	// parse the return type, if any
+	if (ret_type == NULL)
+	    fp->uf_ret_type = &t_void;
+	else
+	{
+	    p = ret_type;
+	    fp->uf_ret_type = parse_type(&p, &fp->uf_type_list);
+	}
+    }
+
     fp->uf_lines = newlines;
     if ((flags & FC_CLOSURE) != 0)
     {
@@ -2716,10 +3028,22 @@ ex_function(exarg_T *eap)
     fp->uf_calls = 0;
     fp->uf_script_ctx = current_sctx;
     fp->uf_script_ctx.sc_lnum += sourcing_lnum_top;
+    if (is_export)
+    {
+	fp->uf_flags |= FC_EXPORT;
+	// let ex_export() know the export worked.
+	is_export = FALSE;
+    }
+
+    // ":def Func()" needs to be compiled
+    if (eap->cmdidx == CMD_def)
+	compile_def_function(fp, FALSE);
+
     goto ret_free;
 
 erret:
     ga_clear_strings(&newargs);
+    ga_clear_strings(&argtypes);
     ga_clear_strings(&default_args);
 errret_2:
     ga_clear_strings(&newlines);
@@ -2755,7 +3079,17 @@ translated_function_exists(char_u *name)
 {
     if (builtin_function(name, -1))
 	return has_internal_func(name);
-    return find_func(name) != NULL;
+    return find_func(name, NULL) != NULL;
+}
+
+/*
+ * Return TRUE when "ufunc" has old-style "..." varargs
+ * or named varargs "...name: type".
+ */
+    int
+has_varargs(ufunc_T *ufunc)
+{
+    return ufunc->uf_varargs || ufunc->uf_va_name != NULL;
 }
 
 /*
@@ -2826,9 +3160,10 @@ get_user_func_name(expand_T *xp, int idx
 	    ++hi;
 	fp = HI2UF(hi);
 
-	if ((fp->uf_flags & FC_DICT)
+	// don't show dead, dict and lambda functions
+	if ((fp->uf_flags & FC_DEAD) || (fp->uf_flags & FC_DICT)
 				|| STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
-	    return (char_u *)""; // don't show dict and lambda functions
+	    return (char_u *)"";
 
 	if (STRLEN(fp->uf_name) + 4 >= IOSIZE)
 	    return fp->uf_name;	// prevents overflow
@@ -2837,7 +3172,7 @@ get_user_func_name(expand_T *xp, int idx
 	if (xp->xp_context != EXPAND_USER_FUNC)
 	{
 	    STRCAT(IObuff, "(");
-	    if (!fp->uf_varargs && fp->uf_args.ga_len == 0)
+	    if (!has_varargs(fp) && fp->uf_args.ga_len == 0)
 		STRCAT(IObuff, ")");
 	}
 	return IObuff;
@@ -2876,7 +3211,7 @@ ex_delfunction(exarg_T *eap)
 	*p = NUL;
 
     if (!eap->skip)
-	fp = find_func(name);
+	fp = find_func(name, NULL);
     vim_free(name);
 
     if (!eap->skip)
@@ -2931,7 +3266,7 @@ func_unref(char_u *name)
 
     if (name == NULL || !func_name_refcount(name))
 	return;
-    fp = find_func(name);
+    fp = find_func(name, NULL);
     if (fp == NULL && isdigit(*name))
     {
 #ifdef EXITFREE
@@ -2974,7 +3309,7 @@ func_ref(char_u *name)
 
     if (name == NULL || !func_name_refcount(name))
 	return;
-    fp = find_func(name);
+    fp = find_func(name, NULL);
     if (fp != NULL)
 	++fp->uf_refcount;
     else if (isdigit(*name))
@@ -3119,7 +3454,7 @@ ex_call(exarg_T *eap)
 
     if (*startarg != '(')
     {
-	semsg(_(e_missingparen), eap->arg);
+	semsg(_(e_missing_paren), eap->arg);
 	goto end;
     }
 
@@ -3444,7 +3779,7 @@ make_partial(dict_T *selfdict_in, typval
 					      : rettv->vval.v_partial->pt_name;
 	// Translate "s:func" to the stored function name.
 	fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
-	fp = find_func(fname);
+	fp = find_func(fname, NULL);
 	vim_free(tofree);
     }
 
@@ -3610,7 +3945,7 @@ get_funccal(void)
     hashtab_T *
 get_funccal_local_ht()
 {
-    if (current_funccal == NULL)
+    if (current_funccal == NULL || current_funccal->l_vars.dv_refcount == 0)
 	return NULL;
     return &get_funccal()->l_vars.dv_hashtab;
 }
@@ -3622,7 +3957,7 @@ get_funccal_local_ht()
     dictitem_T *
 get_funccal_local_var()
 {
-    if (current_funccal == NULL)
+    if (current_funccal == NULL || current_funccal->l_vars.dv_refcount == 0)
 	return NULL;
     return &get_funccal()->l_vars_var;
 }
@@ -3634,7 +3969,7 @@ get_funccal_local_var()
     hashtab_T *
 get_funccal_args_ht()
 {
-    if (current_funccal == NULL)
+    if (current_funccal == NULL || current_funccal->l_vars.dv_refcount == 0)
 	return NULL;
     return &get_funccal()->l_avars.dv_hashtab;
 }
@@ -3646,7 +3981,7 @@ get_funccal_args_ht()
     dictitem_T *
 get_funccal_args_var()
 {
-    if (current_funccal == NULL)
+    if (current_funccal == NULL || current_funccal->l_vars.dv_refcount == 0)
 	return NULL;
     return &get_funccal()->l_avars_var;
 }
@@ -3657,7 +3992,7 @@ get_funccal_args_var()
     void
 list_func_vars(int *first)
 {
-    if (current_funccal != NULL)
+    if (current_funccal != NULL && current_funccal->l_vars.dv_refcount > 0)
 	list_hashtable_vars(&current_funccal->l_vars.dv_hashtab,
 							   "l:", FALSE, first);
 }
@@ -3866,7 +4201,7 @@ set_ref_in_func(char_u *name, ufunc_T *f
     if (fp_in == NULL)
     {
 	fname = fname_trans_sid(name, fname_buf, &tofree, &error);
-	fp = find_func(fname);
+	fp = find_func(fname, NULL);
     }
     if (fp != NULL)
     {