patch 8.2.1329: Vim9: cannot define global function inside :def function

Problem:    Vim9: cannot define global function inside :def function.
Solution:   Assign to global variable instead of local. (closes #6584)
diff --git a/src/misc2.c b/src/misc2.c
index 7ef6cdf..3e16c36 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -2028,6 +2028,41 @@
 }
 
 /*
+ * Copy a growing array that contains a list of strings.
+ */
+    int
+ga_copy_strings(garray_T *from, garray_T *to)
+{
+    int		i;
+
+    ga_init2(to, sizeof(char_u *), 1);
+    if (ga_grow(to, from->ga_len) == FAIL)
+	return FAIL;
+
+    for (i = 0; i < from->ga_len; ++i)
+    {
+	char_u *orig = ((char_u **)from->ga_data)[i];
+	char_u *copy;
+
+	if (orig == NULL)
+	    copy = NULL;
+	else
+	{
+	    copy = vim_strsave(orig);
+	    if (copy == NULL)
+	    {
+		to->ga_len = i;
+		ga_clear_strings(to);
+		return FAIL;
+	    }
+	}
+	((char_u **)to->ga_data)[i] = copy;
+    }
+    to->ga_len = from->ga_len;
+    return OK;
+}
+
+/*
  * Initialize a growing array.	Don't forget to set ga_itemsize and
  * ga_growsize!  Or use ga_init2().
  */
diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro
index e1e20aa..d55fc31 100644
--- a/src/proto/misc2.pro
+++ b/src/proto/misc2.pro
@@ -56,6 +56,7 @@
 int vim_isspace(int x);
 void ga_clear(garray_T *gap);
 void ga_clear_strings(garray_T *gap);
+int ga_copy_strings(garray_T *from, garray_T *to);
 void ga_init(garray_T *gap);
 void ga_init2(garray_T *gap, int itemsize, int growsize);
 int ga_grow(garray_T *gap, int n);
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index 9c9eb2d..f30ac2f 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -5,6 +5,7 @@
 char_u *get_lambda_name(void);
 char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
 int get_lambda_tv(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
+void copy_func(char_u *lambda, char_u *global);
 char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload);
 void emsg_funcname(char *ermsg, char_u *name);
 int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
diff --git a/src/structs.h b/src/structs.h
index ba9c98d..7e34d80 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1546,6 +1546,7 @@
 
 /*
  * Structure to hold info for a user function.
+ * When adding a field check copy_func().
  */
 typedef struct
 {
@@ -1618,6 +1619,7 @@
 #define FC_NOARGS   0x200	// no a: variables in lambda
 #define FC_VIM9	    0x400	// defined in vim9 script file
 #define FC_CFUNC    0x800	// defined as Lua C func
+#define FC_COPY	    0x1000	// copy of another function by copy_func()
 
 #define MAX_FUNC_ARGS	20	// maximum number of function arguments
 #define VAR_SHORT_LEN	20	// short variable name length
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 98a9e20..ea012b7 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -699,6 +699,24 @@
         instr)
 enddef
 
+def NestedOuter()
+  def g:Inner()
+    echomsg "inner"
+  enddef
+enddef
+
+def Test_nested_func()
+   let instr = execute('disassemble NestedOuter')
+   assert_match('NestedOuter\_s*' ..
+        'def g:Inner()\_s*' ..
+        'echomsg "inner"\_s*' ..
+        'enddef\_s*' ..
+        '\d NEWFUNC <lambda>\d\+ Inner\_s*' ..
+        '\d PUSHNR 0\_s*' ..
+        '\d RETURN',
+        instr)
+enddef
+
 def AndOr(arg: any): string
   if arg == 1 && arg != 2 || arg == 4
     return 'yes'
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 2546f13..dae6442 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -133,6 +133,28 @@
   CheckDefFailure(['func Nested()', 'endfunc'], 'E1086:')
 enddef
 
+def Test_nested_global_function()
+  let lines =<< trim END
+      vim9script
+      def Outer()
+          def g:Inner(): string
+              return 'inner'
+          enddef
+      enddef
+      disass Outer
+      Outer()
+      assert_equal('inner', g:Inner())
+      delfunc g:Inner
+      Outer()
+      assert_equal('inner', g:Inner())
+      delfunc g:Inner
+      Outer()
+      assert_equal('inner', g:Inner())
+      delfunc g:Inner
+  END
+  CheckScriptSuccess(lines)
+enddef
+
 func Test_call_default_args_from_func()
   call assert_equal('string', MyDefaultArgs())
   call assert_equal('one', MyDefaultArgs('one'))
diff --git a/src/userfunc.c b/src/userfunc.c
index de0cdb7..de7034d 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -366,7 +366,7 @@
     if (fp == NULL)
 	return NULL;
 
-    fp->uf_dfunc_idx = UF_NOT_COMPILED;
+    fp->uf_def_status = UF_NOT_COMPILED;
     fp->uf_refcount = 1;
     fp->uf_varargs = TRUE;
     fp->uf_flags = FC_CFUNC;
@@ -1069,7 +1069,8 @@
     {
 	// 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_def_status == UF_COMPILED)
+	// Do remove it when it's a copy.
+	if (fp->uf_def_status == UF_COMPILED && (fp->uf_flags & FC_COPY) == 0)
 	    fp->uf_flags |= FC_DEAD;
 	else
 	    hash_remove(&func_hashtab, hi);
@@ -1122,7 +1123,8 @@
     // clear this function
     func_clear_items(fp);
     funccal_unref(fp->uf_scoped, fp, force);
-    clear_def_function(fp);
+    if ((fp->uf_flags & FC_COPY) == 0)
+	clear_def_function(fp);
 }
 
 /*
@@ -1150,12 +1152,83 @@
 func_clear_free(ufunc_T *fp, int force)
 {
     func_clear(fp, force);
-    if (force || fp->uf_dfunc_idx == 0)
+    if (force || fp->uf_dfunc_idx == 0 || (fp->uf_flags & FC_COPY))
 	func_free(fp, force);
     else
 	fp->uf_flags |= FC_DEAD;
 }
 
+/*
+ * Copy already defined function "lambda" to a new function with name "global".
+ * This is for when a compiled function defines a global function.
+ */
+    void
+copy_func(char_u *lambda, char_u *global)
+{
+    ufunc_T *ufunc = find_func_even_dead(lambda, TRUE, NULL);
+    ufunc_T *fp;
+
+    if (ufunc == NULL)
+	semsg(_("E1102: lambda function not found: %s"), lambda);
+    else
+    {
+	// TODO: handle ! to overwrite
+	fp = find_func(global, TRUE, NULL);
+	if (fp != NULL)
+	{
+	    semsg(_(e_funcexts), global);
+	    return;
+	}
+
+	fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(global) + 1);
+	if (fp == NULL)
+	    return;
+
+	fp->uf_varargs = ufunc->uf_varargs;
+	fp->uf_flags = (ufunc->uf_flags & ~FC_VIM9) | FC_COPY;
+	fp->uf_def_status = ufunc->uf_def_status;
+	fp->uf_dfunc_idx = ufunc->uf_dfunc_idx;
+	if (ga_copy_strings(&fp->uf_args, &ufunc->uf_args) == FAIL
+		|| ga_copy_strings(&fp->uf_def_args, &ufunc->uf_def_args)
+									== FAIL
+		|| ga_copy_strings(&fp->uf_lines, &ufunc->uf_lines) == FAIL)
+	    goto failed;
+
+	fp->uf_name_exp = ufunc->uf_name_exp == NULL ? NULL
+					     : vim_strsave(ufunc->uf_name_exp);
+	if (ufunc->uf_arg_types != NULL)
+	{
+	    fp->uf_arg_types = ALLOC_MULT(type_T *, fp->uf_args.ga_len);
+	    if (fp->uf_arg_types == NULL)
+		goto failed;
+	    mch_memmove(fp->uf_arg_types, ufunc->uf_arg_types,
+					sizeof(type_T *) * fp->uf_args.ga_len);
+	}
+	if (ufunc->uf_def_arg_idx != NULL)
+	{
+	    fp->uf_def_arg_idx = ALLOC_MULT(int, fp->uf_def_args.ga_len + 1);
+	    if (fp->uf_def_arg_idx == NULL)
+		goto failed;
+	    mch_memmove(fp->uf_def_arg_idx, ufunc->uf_def_arg_idx,
+				     sizeof(int) * fp->uf_def_args.ga_len + 1);
+	}
+	if (ufunc->uf_va_name != NULL)
+	{
+	    fp->uf_va_name = vim_strsave(ufunc->uf_va_name);
+	    if (fp->uf_va_name == NULL)
+		goto failed;
+	}
+
+	fp->uf_refcount = 1;
+	STRCPY(fp->uf_name, global);
+	hash_add(&func_hashtab, UF2HIKEY(fp));
+    }
+    return;
+
+failed:
+    func_clear_free(fp, TRUE);
+}
+
 
 /*
  * Call a user function.
@@ -2521,6 +2594,8 @@
 
 /*
  * ":function" also supporting nested ":def".
+ * When "name_arg" is not NULL this is a nested function, using "name_arg" for
+ * the function name.
  * Returns a pointer to the function or NULL if no function defined.
  */
     ufunc_T *
diff --git a/src/version.c b/src/version.c
index 6a939f4..75232bf 100644
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1329,
+/**/
     1328,
 /**/
     1327,
diff --git a/src/vim9.h b/src/vim9.h
index 0d51c98..77f3427 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -79,6 +79,7 @@
     ISN_PCALL_END,  // cleanup after ISN_PCALL with cpf_top set
     ISN_RETURN,	    // return, result is on top of stack
     ISN_FUNCREF,    // push a function ref to dfunc isn_arg.funcref
+    ISN_NEWFUNC,    // create a global function from a lambda function
 
     // expression operations
     ISN_JUMP,	    // jump if condition is matched isn_arg.jump
@@ -237,6 +238,12 @@
     int		fr_var_idx;	// variable to store partial
 } funcref_T;
 
+// arguments to ISN_NEWFUNC
+typedef struct {
+    char_u	*nf_lambda;	// name of the lambda already defined
+    char_u	*nf_global;	// name of the global function to be created
+} newfunc_T;
+
 // arguments to ISN_CHECKLEN
 typedef struct {
     int		cl_min_len;	// minimum length
@@ -281,6 +288,7 @@
 	script_T	    script;
 	unlet_T		    unlet;
 	funcref_T	    funcref;
+	newfunc_T	    newfunc;
 	checklen_T	    checklen;
 	shuffle_T	    shuffle;
     } isn_arg;
diff --git a/src/vim9compile.c b/src/vim9compile.c
index cf08e95..b9b2b6f 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1523,6 +1523,27 @@
 }
 
 /*
+ * Generate an ISN_NEWFUNC instruction.
+ */
+    static int
+generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
+{
+    isn_T	*isn;
+    char_u	*name;
+
+    RETURN_OK_IF_SKIP(cctx);
+    name = vim_strsave(lambda_name);
+    if (name == NULL)
+	return FAIL;
+    if ((isn = generate_instr(cctx, ISN_NEWFUNC)) == NULL)
+	return FAIL;
+    isn->isn_arg.newfunc.nf_lambda = name;
+    isn->isn_arg.newfunc.nf_global = func_name;
+
+    return OK;
+}
+
+/*
  * Generate an ISN_JUMP instruction.
  */
     static int
@@ -4875,11 +4896,13 @@
     static char_u *
 compile_nested_function(exarg_T *eap, cctx_T *cctx)
 {
+    int		is_global = *eap->arg == 'g' && eap->arg[1] == ':';
     char_u	*name_start = eap->arg;
-    char_u	*name_end = to_name_end(eap->arg, FALSE);
+    char_u	*name_end = to_name_end(eap->arg, is_global);
     char_u	*name = get_lambda_name();
     lvar_T	*lvar;
     ufunc_T	*ufunc;
+    int		r;
 
     eap->arg = name_end;
     eap->getline = exarg_getline;
@@ -4894,16 +4917,28 @@
 	    && compile_def_function(ufunc, TRUE, cctx) == FAIL)
 	return NULL;
 
-    // Define a local variable for the function reference.
-    lvar = reserve_local(cctx, name_start, name_end - name_start,
-						    TRUE, ufunc->uf_func_type);
+    if (is_global)
+    {
+	char_u *func_name = vim_strnsave(name_start + 2,
+						    name_end - name_start - 2);
 
-    if (generate_FUNCREF(cctx, ufunc->uf_dfunc_idx) == FAIL
-	    || generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL) == FAIL)
-	return NULL;
+	if (func_name == NULL)
+	    r = FAIL;
+	else
+	    r = generate_NEWFUNC(cctx, name, func_name);
+    }
+    else
+    {
+	// Define a local variable for the function reference.
+	lvar = reserve_local(cctx, name_start, name_end - name_start,
+						    TRUE, ufunc->uf_func_type);
+	if (generate_FUNCREF(cctx, ufunc->uf_dfunc_idx) == FAIL)
+	    return NULL;
+	r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
+    }
 
     // TODO: warning for trailing text?
-    return (char_u *)"";
+    return r == FAIL ? NULL : (char_u *)"";
 }
 
 /*
@@ -7641,6 +7676,11 @@
 	    }
 	    break;
 
+	case ISN_NEWFUNC:
+	    vim_free(isn->isn_arg.newfunc.nf_lambda);
+	    vim_free(isn->isn_arg.newfunc.nf_global);
+	    break;
+
 	case ISN_2BOOL:
 	case ISN_2STRING:
 	case ISN_ADDBLOB:
diff --git a/src/vim9execute.c b/src/vim9execute.c
index e52df61..9bfec3a 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -723,7 +723,10 @@
 	dfunc_T	*dfunc = ((dfunc_T *)def_functions.ga_data)
 							 + ufunc->uf_dfunc_idx;
 	if (dfunc->df_instr == NULL)
+	{
+	    iemsg("using call_def_function() on not compiled function");
 	    return FAIL;
+	}
     }
 
     CLEAR_FIELD(ectx);
@@ -1726,6 +1729,15 @@
 		}
 		break;
 
+	    // Create a global function from a lambda.
+	    case ISN_NEWFUNC:
+		{
+		    newfunc_T	*newfunc = &iptr->isn_arg.newfunc;
+
+		    copy_func(newfunc->nf_lambda, newfunc->nf_global);
+		}
+		break;
+
 	    // jump if a condition is met
 	    case ISN_JUMP:
 		{
@@ -2912,6 +2924,15 @@
 		}
 		break;
 
+	    case ISN_NEWFUNC:
+		{
+		    newfunc_T	*newfunc = &iptr->isn_arg.newfunc;
+
+		    smsg("%4d NEWFUNC %s %s", current,
+				       newfunc->nf_lambda, newfunc->nf_global);
+		}
+		break;
+
 	    case ISN_JUMP:
 		{
 		    char *when = "?";