patch 9.1.0367: compile_def_function is too long

Commit: 
https://github.com/vim/vim/commit/a16f251333e324c94ca8e3e92d1fcf3193dfa382
Author: Yegappan Lakshmanan <yegap...@yahoo.com>
Date:   Tue Apr 23 20:14:46 2024 +0200

    patch 9.1.0367: compile_def_function is too long
    
    Problem:  compile_def_function is too long
    Solution: Move out the code to compile the body of a function
              (Yegappan Lakshmanan)
    
    closes: #14622
    
    Signed-off-by: Yegappan Lakshmanan <yegap...@yahoo.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/testdir/test_vim9_builtin.vim 
b/src/testdir/test_vim9_builtin.vim
index 35ff091a4..42d09fe9a 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -4785,6 +4785,8 @@ def Test_typename()
   endif
   assert_equal('class<Unknown>', typename(null_class))
   assert_equal('object<Unknown>', typename(null_object))
+  var l: list<func(list<number>): number> = [function('min')]
+  assert_equal('list<func(list<number>): number>', typename(l))
 enddef
 
 def Test_undofile()
diff --git a/src/version.c b/src/version.c
index 7113699f3..fc3969164 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    367,
 /**/
     366,
 /**/
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 0e05f820c..a091c5796 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -3271,261 +3271,25 @@ add_def_function(ufunc_T *ufunc)
 }
 
 /*
- * After ex_function() has collected all the function lines: parse and compile
- * the lines into instructions.
- * Adds the function to "def_functions".
- * When "check_return_type" is set then set ufunc->uf_ret_type to the type of
- * the return statement (used for lambda).  When uf_ret_type is already set
- * then check that it matches.
- * When "profiling" is true add ISN_PROF_START instructions.
- * "outer_cctx" is set for a nested function.
- * This can be used recursively through compile_lambda(), which may reallocate
- * "def_functions".
- * Returns OK or FAIL.
+ * Compile def function body.  Loop over all the lines in the function and
+ * generate instructions.
  */
-    int
-compile_def_function(
-       ufunc_T         *ufunc,
-       int             check_return_type,
-       compiletype_T   compile_type,
-       cctx_T          *outer_cctx)
+    static int
+compile_def_function_body(
+    cctx_T     *cctx,
+    int                last_func_lnum,
+    int                check_return_type,
+    garray_T   *lines_to_free,
+    char       **errormsg)
 {
     char_u     *line = NULL;
-    garray_T   lines_to_free;
     char_u     *p;
-    char       *errormsg = NULL;       // error message
-    cctx_T     cctx;
-    garray_T   *instr;
     int                did_emsg_before = did_emsg;
-    int                did_emsg_silent_before = did_emsg_silent;
-    int                ret = FAIL;
-    sctx_T     save_current_sctx = current_sctx;
-    int                save_estack_compiling = estack_compiling;
-    int                save_cmod_flags = cmdmod.cmod_flags;
-    int                do_estack_push;
-    int                new_def_function = FALSE;
 #ifdef FEAT_PROFILE
     int                prof_lnum = -1;
 #endif
     int                debug_lnum = -1;
 
-    // allocated lines are freed at the end
-    ga_init2(&lines_to_free, sizeof(char_u *), 50);
-
-    // When using a function that was compiled before: Free old instructions.
-    // The index is reused.  Otherwise add a new entry in "def_functions".
-    if (ufunc->uf_dfunc_idx > 0)
-    {
-       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
-                                                        + ufunc->uf_dfunc_idx;
-       isn_T   *instr_dest = NULL;
-
-       switch (compile_type)
-       {
-           case CT_PROFILE:
-#ifdef FEAT_PROFILE
-                           instr_dest = dfunc->df_instr_prof; break;
-#endif
-           case CT_NONE:   instr_dest = dfunc->df_instr; break;
-           case CT_DEBUG:  instr_dest = dfunc->df_instr_debug; break;
-       }
-       if (instr_dest != NULL)
-           // Was compiled in this mode before: Free old instructions.
-           delete_def_function_contents(dfunc, FALSE);
-       ga_clear_strings(&dfunc->df_var_names);
-       dfunc->df_defer_var_idx = 0;
-    }
-    else
-    {
-       if (add_def_function(ufunc) == FAIL)
-           return FAIL;
-       new_def_function = TRUE;
-    }
-
-    if ((ufunc->uf_flags & FC_CLOSURE) && outer_cctx == NULL)
-    {
-       semsg(_(e_compiling_closure_without_context_str),
-                                                  printable_func_name(ufunc));
-       return FAIL;
-    }
-
-    ufunc->uf_def_status = UF_COMPILING;
-
-    CLEAR_FIELD(cctx);
-
-    cctx.ctx_compile_type = compile_type;
-    cctx.ctx_ufunc = ufunc;
-    cctx.ctx_lnum = -1;
-    cctx.ctx_outer = outer_cctx;
-    ga_init2(&cctx.ctx_locals, sizeof(lvar_T), 10);
-    // Each entry on the type stack consists of two type pointers.
-    ga_init2(&cctx.ctx_type_stack, sizeof(type2_T), 50);
-    cctx.ctx_type_list = &ufunc->uf_type_list;
-    ga_init2(&cctx.ctx_instr, sizeof(isn_T), 50);
-    instr = &cctx.ctx_instr;
-
-    // Set the context to the function, it may be compiled when called from
-    // another script.  Set the script version to the most modern one.
-    // The line number will be set in next_line_from_context().
-    current_sctx = ufunc->uf_script_ctx;
-    current_sctx.sc_version = SCRIPT_VERSION_VIM9;
-
-    // Don't use the flag from ":legacy" here.
-    cmdmod.cmod_flags &= ~CMOD_LEGACY;
-
-    // Make sure error messages are OK.
-    do_estack_push = !estack_top_is_ufunc(ufunc, 1);
-    if (do_estack_push)
-       estack_push_ufunc(ufunc, 1);
-    estack_compiling = TRUE;
-
-    if (check_args_shadowing(ufunc, &cctx) == FAIL)
-       goto erret;
-
-    // For an object method and constructor "this" is the first local variable.
-    if (ufunc->uf_flags & (FC_OBJECT|FC_NEW))
-    {
-       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
-                                                        + ufunc->uf_dfunc_idx;
-       if (GA_GROW_FAILS(&dfunc->df_var_names, 1))
-           goto erret;
-       ((char_u **)dfunc->df_var_names.ga_data)[0] =
-                                                vim_strsave((char_u *)"this");
-       ++dfunc->df_var_names.ga_len;
-
-       // In the constructor allocate memory for the object and initialize the
-       // object members.
-       if (IS_CONSTRUCTOR_METHOD(ufunc))
-       {
-           generate_CONSTRUCT(&cctx, ufunc->uf_class);
-
-           for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
-           {
-               ocmember_T *m = &ufunc->uf_class->class_obj_members[i];
-
-               if (i < 2 && IS_ENUM(ufunc->uf_class))
-                   // The first two object variables in an enum are the name
-                   // and the ordinal.  These are set by the ISN_CONSTRUCT
-                   // instruction.  So don't generate instructions to set
-                   // these variables.
-                   continue;
-
-               if (m->ocm_init != NULL)
-               {
-                   char_u *expr = m->ocm_init;
-                   if (compile_expr0(&expr, &cctx) == FAIL)
-                       goto erret;
-                   if (!ends_excmd2(m->ocm_init, expr))
-                   {
-                       semsg(_(e_trailing_characters_str), expr);
-                       goto erret;
-                   }
-
-                   type_T      *type = get_type_on_stack(&cctx, 0);
-                   if (m->ocm_type->tt_type == VAR_ANY
-                           && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
-                           && type->tt_type != VAR_SPECIAL)
-                   {
-                       // If the member variable type is not yet set, then use
-                       // the initialization expression type.
-                       m->ocm_type = type;
-                   }
-                   else if (m->ocm_type->tt_type != type->tt_type)
-                   {
-                       // The type of the member initialization expression is
-                       // determined at run time.  Add a runtime type check.
-                       where_T where = WHERE_INIT;
-                       where.wt_kind = WT_MEMBER;
-                       where.wt_func_name = (char *)m->ocm_name;
-                       if (need_type_where(type, m->ocm_type, FALSE, -1,
-                                   where, &cctx, FALSE, FALSE) == FAIL)
-                           goto erret;
-                   }
-               }
-               else
-                   push_default_value(&cctx, m->ocm_type->tt_type,
-                                                                 FALSE, NULL);
-               generate_STORE_THIS(&cctx, i);
-           }
-       }
-    }
-
-    if (ufunc->uf_def_args.ga_len > 0)
-    {
-       int     count = ufunc->uf_def_args.ga_len;
-       int     first_def_arg = ufunc->uf_args.ga_len - count;
-       int     i;
-       int     off = STACK_FRAME_SIZE + (ufunc->uf_va_name != NULL ? 1 : 0);
-       int     did_set_arg_type = FALSE;
-
-       // Produce instructions for the default values of optional arguments.
-       SOURCING_LNUM = 0;  // line number unknown
-       for (i = 0; i < count; ++i)
-       {
-           char_u *arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
-           if (STRCMP(arg, "v:none") == 0)
-               // "arg = v:none" means the argument is optional without
-               // setting a value when the argument is missing.
-               continue;
-
-           type_T      *val_type;
-           int         arg_idx = first_def_arg + i;
-           where_T     where = WHERE_INIT;
-           int         jump_instr_idx = instr->ga_len;
-           isn_T       *isn;
-
-           // Use a JUMP_IF_ARG_SET instruction to skip if the value was given.
-           if (generate_JUMP_IF_ARG(&cctx, ISN_JUMP_IF_ARG_SET,
-                                                     i - count - off) == FAIL)
-               goto erret;
-
-           // Make sure later arguments are not found.
-           ufunc->uf_args_visible = arg_idx;
-
-           int r = compile_expr0(&arg, &cctx);
-           if (r == FAIL)
-               goto erret;
-
-           // If no type specified use the type of the default value.
-           // Otherwise check that the default value type matches the
-           // specified type.
-           val_type = get_type_on_stack(&cctx, 0);
-           where.wt_index = arg_idx + 1;
-           where.wt_kind = WT_ARGUMENT;
-           if (ufunc->uf_arg_types[arg_idx] == &t_unknown)
-           {
-               did_set_arg_type = TRUE;
-               ufunc->uf_arg_types[arg_idx] = val_type;
-           }
-           else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx],
-                               FALSE, -1, where, &cctx, FALSE, FALSE) == FAIL)
-               goto erret;
-
-           if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL)
-               goto erret;
-
-           // set instruction index in JUMP_IF_ARG_SET to here
-           isn = ((isn_T *)instr->ga_data) + jump_instr_idx;
-           isn->isn_arg.jumparg.jump_where = instr->ga_len;
-       }
-
-       if (did_set_arg_type)
-           set_function_type(ufunc);
-    }
-    ufunc->uf_args_visible = ufunc->uf_args.ga_len;
-
-    // Compiling a function in an interface is done to get the function type.
-    // No code is actually compiled.
-    if (ufunc->uf_class != NULL && IS_INTERFACE(ufunc->uf_class))
-    {
-       ufunc->uf_def_status = UF_NOT_COMPILED;
-       ret = OK;
-       goto erret;
-    }
-
-    /*
-     * Loop over all the lines of the function and generate instructions.
-     */
     for (;;)
     {
        exarg_T     ea;
@@ -3536,29 +3300,29 @@ compile_def_function(
        // Bail out on the first error to avoid a flood of errors and report
        // the right line number when inside try/catch.
        if (did_emsg_before != did_emsg)
-           goto erret;
+           return FAIL;
 
        if (line != NULL && *line == '|')
            // the line continues after a '|'
            ++line;
        else if (line != NULL && *skipwhite(line) != NUL
-               && !(*line == '#' && (line == cctx.ctx_line_start
+               && !(*line == '#' && (line == cctx->ctx_line_start
                                                    || VIM_ISWHITE(line[-1]))))
        {
            semsg(_(e_trailing_characters_str), line);
-           goto erret;
+           return FAIL;
        }
        else if (line != NULL && vim9_bad_comment(skipwhite(line)))
-           goto erret;
+           return FAIL;
        else
        {
-           line = next_line_from_context(&cctx, FALSE);
-           if (cctx.ctx_lnum >= ufunc->uf_lines.ga_len)
+           line = next_line_from_context(cctx, FALSE);
+           if (cctx->ctx_lnum >= last_func_lnum)
            {
                // beyond the last line
 #ifdef FEAT_PROFILE
-               if (cctx.ctx_skip != SKIP_YES)
-                   may_generate_prof_end(&cctx, prof_lnum);
+               if (cctx->ctx_skip != SKIP_YES)
+                   may_generate_prof_end(cctx, prof_lnum);
 #endif
                break;
            }
@@ -3567,42 +3331,42 @@ compile_def_function(
            if (line != NULL)
            {
                line = vim_strsave(line);
-               if (ga_add_string(&lines_to_free, line) == FAIL)
-                   goto erret;
+               if (ga_add_string(lines_to_free, line) == FAIL)
+                   return FAIL;
            }
        }
 
        CLEAR_FIELD(ea);
        ea.cmdlinep = &line;
        ea.cmd = skipwhite(line);
-       ea.skip = cctx.ctx_skip == SKIP_YES;
+       ea.skip = cctx->ctx_skip == SKIP_YES;
 
        if (*ea.cmd == '#')
        {
            // "#" starts a comment, but "#{" is an error
            if (vim9_bad_comment(ea.cmd))
-               goto erret;
+               return FAIL;
            line = (char_u *)"";
            continue;
        }
 
 #ifdef FEAT_PROFILE
-       if (cctx.ctx_compile_type == CT_PROFILE && cctx.ctx_lnum != prof_lnum
-                                                 && cctx.ctx_skip != SKIP_YES)
+       if (cctx->ctx_compile_type == CT_PROFILE && cctx->ctx_lnum != prof_lnum
+                                                 && cctx->ctx_skip != SKIP_YES)
        {
-           may_generate_prof_end(&cctx, prof_lnum);
+           may_generate_prof_end(cctx, prof_lnum);
 
-           prof_lnum = cctx.ctx_lnum;
-           generate_instr(&cctx, ISN_PROF_START);
+           prof_lnum = cctx->ctx_lnum;
+           generate_instr(cctx, ISN_PROF_START);
        }
 #endif
-       if (cctx.ctx_compile_type == CT_DEBUG && cctx.ctx_lnum != debug_lnum
-                                                 && cctx.ctx_skip != SKIP_YES)
+       if (cctx->ctx_compile_type == CT_DEBUG && cctx->ctx_lnum != debug_lnum
+                                                 && cctx->ctx_skip != SKIP_YES)
        {
-           debug_lnum = cctx.ctx_lnum;
-           generate_instr_debug(&cctx);
+           debug_lnum = cctx->ctx_lnum;
+           generate_instr_debug(cctx);
        }
-       cctx.ctx_prev_lnum = cctx.ctx_lnum + 1;
+       cctx->ctx_prev_lnum = cctx->ctx_lnum + 1;
 
        // Some things can be recognized by the first character.
        switch (*ea.cmd)
@@ -3610,18 +3374,18 @@ compile_def_function(
            case '}':
                {
                    // "}" ends a block scope
-                   scopetype_T stype = cctx.ctx_scope == NULL
-                                         ? NO_SCOPE : cctx.ctx_scope->se_type;
+                   scopetype_T stype = cctx->ctx_scope == NULL
+                                         ? NO_SCOPE : cctx->ctx_scope->se_type;
 
                    if (stype == BLOCK_SCOPE)
                    {
-                       compile_endblock(&cctx);
+                       compile_endblock(cctx);
                        line = ea.cmd;
                    }
                    else
                    {
                        emsg(_(e_using_rcurly_outside_if_block_scope));
-                       goto erret;
+                       return FAIL;
                    }
                    if (line != NULL)
                        line = skipwhite(ea.cmd + 1);
@@ -3633,7 +3397,7 @@ compile_def_function(
                // "{'a': 1}->func() is something else
                if (ends_excmd(*skipwhite(ea.cmd + 1)))
                {
-                   line = compile_block(ea.cmd, &cctx);
+                   line = compile_block(ea.cmd, cctx);
                    continue;
                }
                break;
@@ -3642,11 +3406,11 @@ compile_def_function(
        /*
         * COMMAND MODIFIERS
         */
-       cctx.ctx_has_cmdmod = FALSE;
-       if (parse_command_modifiers(&ea, &errormsg, &local_cmdmod, FALSE)
+       cctx->ctx_has_cmdmod = FALSE;
+       if (parse_command_modifiers(&ea, errormsg, &local_cmdmod, FALSE)
                                                                       == FAIL)
-           goto erret;
-       generate_cmdmods(&cctx, &local_cmdmod);
+           return FAIL;
+       generate_cmdmods(cctx, &local_cmdmod);
        undo_cmdmod(&local_cmdmod);
 
        // Check if there was a colon after the last command modifier or before
@@ -3677,11 +3441,11 @@ compile_def_function(
                int         assign;
 
                // Check for assignment after command modifiers.
-               assign = may_compile_assignment(&ea, &line, &cctx);
+               assign = may_compile_assignment(&ea, &line, cctx);
                if (assign == OK)
                    goto nextline;
                if (assign == FAIL)
-                   goto erret;
+                   return FAIL;
            }
        }
 
@@ -3709,13 +3473,13 @@ compile_def_function(
                                   && !(local_cmdmod.cmod_flags & CMOD_LEGACY))
                {
                    semsg(_(e_colon_required_before_range_str), cmd);
-                   goto erret;
+                   return FAIL;
                }
                ea.addr_count = 1;
                if (ends_excmd2(line, ea.cmd))
                {
                    // A range without a command: jump to the line.
-                   generate_EXEC(&cctx, ISN_EXECRANGE,
+                   generate_EXEC(cctx, ISN_EXECRANGE,
                                              vim_strnsave(cmd, ea.cmd - cmd));
                    line = ea.cmd;
                    goto nextline;
@@ -3724,13 +3488,13 @@ compile_def_function(
        }
        p = find_ex_command(&ea, NULL,
                starts_with_colon || (local_cmdmod.cmod_flags & CMOD_LEGACY)
-                                                 ? NULL : item_exists, &cctx);
+                                                 ? NULL : item_exists, cctx);
 
        if (p == NULL)
        {
-           if (cctx.ctx_skip != SKIP_YES)
+           if (cctx->ctx_skip != SKIP_YES)
                semsg(_(e_ambiguous_use_of_user_defined_command_str), ea.cmd);
-           goto erret;
+           return FAIL;
        }
 
        // When using ":legacy cmd" always use compile_exec().
@@ -3755,7 +3519,7 @@ compile_def_function(
                case CMD_finally:
                case CMD_endtry:
                        semsg(_(e_cannot_use_legacy_with_command_str), ea.cmd);
-                       goto erret;
+                       return FAIL;
                default: break;
            }
 
@@ -3772,7 +3536,7 @@ compile_def_function(
            // "p" is equal to "ea.cmd" for a valid command.
            if (ea.cmdidx == CMD_eval || ea.cmdidx == CMD_var)
                ;
-           else if (cctx.ctx_skip == SKIP_YES)
+           else if (cctx->ctx_skip == SKIP_YES)
            {
                line += STRLEN(line);
                goto nextline;
@@ -3780,11 +3544,11 @@ compile_def_function(
            else
            {
                semsg(_(e_command_not_recognized_str), ea.cmd);
-               goto erret;
+               return FAIL;
            }
        }
 
-       if ((cctx.ctx_had_return || cctx.ctx_had_throw)
+       if ((cctx->ctx_had_return || cctx->ctx_had_throw)
                && ea.cmdidx != CMD_elseif
                && ea.cmdidx != CMD_else
                && ea.cmdidx != CMD_endif
@@ -3796,10 +3560,10 @@ compile_def_function(
                && !ignore_unreachable_code_for_testing)
        {
            semsg(_(e_unreachable_code_after_str),
-                                    cctx.ctx_had_return ? "return" : "throw");
-           goto erret;
+                                    cctx->ctx_had_return ? "return" : "throw");
+           return FAIL;
        }
-       cctx.ctx_had_throw = FALSE;
+       cctx->ctx_had_throw = FALSE;
 
        p = skipwhite(p);
        if (ea.cmdidx != CMD_SIZE
@@ -3815,7 +3579,7 @@ compile_def_function(
            if ((ea.argt & EX_RANGE) == 0 && ea.addr_count > 0)
            {
                emsg(_(e_no_range_allowed));
-               goto erret;
+               return FAIL;
            }
        }
 
@@ -3824,13 +3588,13 @@ compile_def_function(
            case CMD_def:
            case CMD_function:
                    ea.arg = p;
-                   line = compile_nested_function(&ea, &cctx, &lines_to_free);
+                   line = compile_nested_function(&ea, cctx, lines_to_free);
                    break;
 
            case CMD_return:
                    line = compile_return(p, check_return_type,
-                                local_cmdmod.cmod_flags & CMOD_LEGACY, &cctx);
-                   cctx.ctx_had_return = TRUE;
+                                local_cmdmod.cmod_flags & CMOD_LEGACY, cctx);
+                   cctx->ctx_had_return = TRUE;
                    break;
 
            case CMD_let:
@@ -3841,7 +3605,7 @@ compile_def_function(
            case CMD_const:
            case CMD_increment:
            case CMD_decrement:
-                   line = compile_assignment(p, &ea, ea.cmdidx, &cctx);
+                   line = compile_assignment(p, &ea, ea.cmdidx, cctx);
                    if (line == p)
                    {
                        emsg(_(e_invalid_assignment));
@@ -3852,7 +3616,7 @@ compile_def_function(
            case CMD_unlet:
            case CMD_unlockvar:
            case CMD_lockvar:
-                   line = compile_unletlock(p, &ea, &cctx);
+                   line = compile_unletlock(p, &ea, cctx);
                    break;
 
            case CMD_import:
@@ -3861,67 +3625,67 @@ compile_def_function(
                    break;
 
            case CMD_if:
-                   line = compile_if(p, &cctx);
+                   line = compile_if(p, cctx);
                    break;
            case CMD_elseif:
-                   line = compile_elseif(p, &cctx);
-                   cctx.ctx_had_return = FALSE;
+                   line = compile_elseif(p, cctx);
+                   cctx->ctx_had_return = FALSE;
                    break;
            case CMD_else:
-                   line = compile_else(p, &cctx);
-                   cctx.ctx_had_return = FALSE;
+                   line = compile_else(p, cctx);
+                   cctx->ctx_had_return = FALSE;
                    break;
            case CMD_endif:
-                   line = compile_endif(p, &cctx);
+                   line = compile_endif(p, cctx);
                    break;
 
            case CMD_while:
-                   line = compile_while(p, &cctx);
+                   line = compile_while(p, cctx);
                    break;
            case CMD_endwhile:
-                   line = compile_endwhile(p, &cctx);
-                   cctx.ctx_had_return = FALSE;
+                   line = compile_endwhile(p, cctx);
+                   cctx->ctx_had_return = FALSE;
                    break;
 
            case CMD_for:
-                   line = compile_for(p, &cctx);
+                   line = compile_for(p, cctx);
                    break;
            case CMD_endfor:
-                   line = compile_endfor(p, &cctx);
-                   cctx.ctx_had_return = FALSE;
+                   line = compile_endfor(p, cctx);
+                   cctx->ctx_had_return = FALSE;
                    break;
            case CMD_continue:
-                   line = compile_continue(p, &cctx);
+                   line = compile_continue(p, cctx);
                    break;
            case CMD_break:
-                   line = compile_break(p, &cctx);
+                   line = compile_break(p, cctx);
                    break;
 
            case CMD_try:
-                   line = compile_try(p, &cctx);
+                   line = compile_try(p, cctx);
                    break;
            case CMD_catch:
-                   line = compile_catch(p, &cctx);
-                   cctx.ctx_had_return = FALSE;
+                   line = compile_catch(p, cctx);
+                   cctx->ctx_had_return = FALSE;
                    break;
            case CMD_finally:
-                   line = compile_finally(p, &cctx);
-                   cctx.ctx_had_return = FALSE;
+                   line = compile_finally(p, cctx);
+                   cctx->ctx_had_return = FALSE;
                    break;
            case CMD_endtry:
-                   line = compile_endtry(p, &cctx);
+                   line = compile_endtry(p, cctx);
                    break;
            case CMD_throw:
-                   line = compile_throw(p, &cctx);
-                   cctx.ctx_had_throw = TRUE;
+                   line = compile_throw(p, cctx);
+                   cctx->ctx_had_throw = TRUE;
                    break;
 
            case CMD_eval:
-                   line = compile_eval(p, &cctx);
+                   line = compile_eval(p, cctx);
                    break;
 
            case CMD_defer:
-                   line = compile_defer(p, &cctx);
+                   line = compile_defer(p, cctx);
                    break;
 
 #ifdef HAS_MESSAGE_WINDOW
@@ -3932,7 +3696,7 @@ compile_def_function(
                            line = NULL;
                        else
                            line = compile_mult_expr(p, ea.cmdidx,
-                                                            cmd_count, &cctx);
+                                                            cmd_count, cctx);
                    }
                    break;
 #endif
@@ -3942,29 +3706,29 @@ compile_def_function(
            case CMD_echoerr:
            case CMD_echomsg:
            case CMD_execute:
-                   line = compile_mult_expr(p, ea.cmdidx, 0, &cctx);
+                   line = compile_mult_expr(p, ea.cmdidx, 0, cctx);
                    break;
 
            case CMD_put:
                    ea.cmd = cmd;
-                   line = compile_put(p, &ea, &cctx);
+                   line = compile_put(p, &ea, cctx);
                    break;
 
            case CMD_substitute:
                    if (check_global_and_subst(ea.cmd, p) == FAIL)
-                       goto erret;
-                   if (cctx.ctx_skip == SKIP_YES)
+                       return FAIL;
+                   if (cctx->ctx_skip == SKIP_YES)
                        line = (char_u *)"";
                    else
                    {
                        ea.arg = p;
-                       line = compile_substitute(line, &ea, &cctx);
+                       line = compile_substitute(line, &ea, cctx);
                    }
                    break;
 
            case CMD_redir:
                    ea.arg = p;
-                   line = compile_redir(line, &ea, &cctx);
+                   line = compile_redir(line, &ea, cctx);
                    break;
 
            case CMD_cexpr:
@@ -3975,7 +3739,7 @@ compile_def_function(
            case CMD_lgetexpr:
 #ifdef FEAT_QUICKFIX
                    ea.arg = p;
-                   line = compile_cexpr(line, &ea, &cctx);
+                   line = compile_cexpr(line, &ea, cctx);
 #else
                    ex_ni(&ea);
                    line = NULL;
@@ -3989,13 +3753,13 @@ compile_def_function(
            case CMD_t:
            case CMD_xit:
                    not_in_vim9(&ea);
-                   goto erret;
+                   return FAIL;
 
            case CMD_SIZE:
-                   if (cctx.ctx_skip != SKIP_YES)
+                   if (cctx->ctx_skip != SKIP_YES)
                    {
                        semsg(_(e_invalid_command_str), ea.cmd);
-                       goto erret;
+                       return FAIL;
                    }
                    // We don't check for a next command here.
                    line = (char_u *)"";
@@ -4012,55 +3776,309 @@ compile_def_function(
            case CMD_tcl:
                    ea.arg = p;
                    if (vim_strchr(line, '
') == NULL)
-                       line = compile_exec(line, &ea, &cctx);
+                       line = compile_exec(line, &ea, cctx);
                    else
                        // heredoc lines have been concatenated with NL
                        // characters in get_function_body()
-                       line = compile_script(line, &cctx);
+                       line = compile_script(line, cctx);
                    break;
 
            case CMD_vim9script:
-                   if (cctx.ctx_skip != SKIP_YES)
+                   if (cctx->ctx_skip != SKIP_YES)
                    {
                        emsg(_(e_vim9script_can_only_be_used_in_script));
-                       goto erret;
+                       return FAIL;
                    }
                    line = (char_u *)"";
                    break;
 
            case CMD_class:
                    emsg(_(e_class_can_only_be_used_in_script));
-                   goto erret;
+                   return FAIL;
 
            case CMD_type:
                    emsg(_(e_type_can_only_be_used_in_script));
-                   goto erret;
+                   return FAIL;
 
            case CMD_global:
                    if (check_global_and_subst(ea.cmd, p) == FAIL)
-                       goto erret;
+                       return FAIL;
                    // FALLTHROUGH
            default:
                    // Not recognized, execute with do_cmdline_cmd().
                    ea.arg = p;
-                   line = compile_exec(line, &ea, &cctx);
+                   line = compile_exec(line, &ea, cctx);
                    break;
        }
 nextline:
        if (line == NULL)
-           goto erret;
+           return FAIL;
        line = skipwhite(line);
 
        // Undo any command modifiers.
-       generate_undo_cmdmods(&cctx);
+       generate_undo_cmdmods(cctx);
 
-       if (cctx.ctx_type_stack.ga_len < 0)
+       if (cctx->ctx_type_stack.ga_len < 0)
        {
            iemsg("Type stack underflow");
-           goto erret;
+           return FAIL;
        }
     } // END of the loop over all the function body lines.
 
+    return OK;
+}
+
+/*
+ * After ex_function() has collected all the function lines: parse and compile
+ * the lines into instructions.
+ * Adds the function to "def_functions".
+ * When "check_return_type" is set then set ufunc->uf_ret_type to the type of
+ * the return statement (used for lambda).  When uf_ret_type is already set
+ * then check that it matches.
+ * When "profiling" is true add ISN_PROF_START instructions.
+ * "outer_cctx" is set for a nested function.
+ * This can be used recursively through compile_lambda(), which may reallocate
+ * "def_functions".
+ * Returns OK or FAIL.
+ */
+    int
+compile_def_function(
+       ufunc_T         *ufunc,
+       int             check_return_type,
+       compiletype_T   compile_type,
+       cctx_T          *outer_cctx)
+{
+    garray_T   lines_to_free;
+    char       *errormsg = NULL;       // error message
+    cctx_T     cctx;
+    garray_T   *instr;
+    int                did_emsg_before = did_emsg;
+    int                did_emsg_silent_before = did_emsg_silent;
+    int                ret = FAIL;
+    sctx_T     save_current_sctx = current_sctx;
+    int                save_estack_compiling = estack_compiling;
+    int                save_cmod_flags = cmdmod.cmod_flags;
+    int                do_estack_push;
+    int                new_def_function = FALSE;
+
+    // allocated lines are freed at the end
+    ga_init2(&lines_to_free, sizeof(char_u *), 50);
+
+    // When using a function that was compiled before: Free old instructions.
+    // The index is reused.  Otherwise add a new entry in "def_functions".
+    if (ufunc->uf_dfunc_idx > 0)
+    {
+       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                                        + ufunc->uf_dfunc_idx;
+       isn_T   *instr_dest = NULL;
+
+       switch (compile_type)
+       {
+           case CT_PROFILE:
+#ifdef FEAT_PROFILE
+                           instr_dest = dfunc->df_instr_prof; break;
+#endif
+           case CT_NONE:   instr_dest = dfunc->df_instr; break;
+           case CT_DEBUG:  instr_dest = dfunc->df_instr_debug; break;
+       }
+       if (instr_dest != NULL)
+           // Was compiled in this mode before: Free old instructions.
+           delete_def_function_contents(dfunc, FALSE);
+       ga_clear_strings(&dfunc->df_var_names);
+       dfunc->df_defer_var_idx = 0;
+    }
+    else
+    {
+       if (add_def_function(ufunc) == FAIL)
+           return FAIL;
+       new_def_function = TRUE;
+    }
+
+    if ((ufunc->uf_flags & FC_CLOSURE) && outer_cctx == NULL)
+    {
+       semsg(_(e_compiling_closure_without_context_str),
+                                                  printable_func_name(ufunc));
+       return FAIL;
+    }
+
+    ufunc->uf_def_status = UF_COMPILING;
+
+    CLEAR_FIELD(cctx);
+
+    cctx.ctx_compile_type = compile_type;
+    cctx.ctx_ufunc = ufunc;
+    cctx.ctx_lnum = -1;
+    cctx.ctx_outer = outer_cctx;
+    ga_init2(&cctx.ctx_locals, sizeof(lvar_T), 10);
+    // Each entry on the type stack consists of two type pointers.
+    ga_init2(&cctx.ctx_type_stack, sizeof(type2_T), 50);
+    cctx.ctx_type_list = &ufunc->uf_type_list;
+    ga_init2(&cctx.ctx_instr, sizeof(isn_T), 50);
+    instr = &cctx.ctx_instr;
+
+    // Set the context to the function, it may be compiled when called from
+    // another script.  Set the script version to the most modern one.
+    // The line number will be set in next_line_from_context().
+    current_sctx = ufunc->uf_script_ctx;
+    current_sctx.sc_version = SCRIPT_VERSION_VIM9;
+
+    // Don't use the flag from ":legacy" here.
+    cmdmod.cmod_flags &= ~CMOD_LEGACY;
+
+    // Make sure error messages are OK.
+    do_estack_push = !estack_top_is_ufunc(ufunc, 1);
+    if (do_estack_push)
+       estack_push_ufunc(ufunc, 1);
+    estack_compiling = TRUE;
+
+    if (check_args_shadowing(ufunc, &cctx) == FAIL)
+       goto erret;
+
+    // For an object method and constructor "this" is the first local variable.
+    if (ufunc->uf_flags & (FC_OBJECT|FC_NEW))
+    {
+       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                                        + ufunc->uf_dfunc_idx;
+       if (GA_GROW_FAILS(&dfunc->df_var_names, 1))
+           goto erret;
+       ((char_u **)dfunc->df_var_names.ga_data)[0] =
+                                                vim_strsave((char_u *)"this");
+       ++dfunc->df_var_names.ga_len;
+
+       // In the constructor allocate memory for the object and initialize the
+       // object members.
+       if (IS_CONSTRUCTOR_METHOD(ufunc))
+       {
+           generate_CONSTRUCT(&cctx, ufunc->uf_class);
+
+           for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
+           {
+               ocmember_T *m = &ufunc->uf_class->class_obj_members[i];
+
+               if (i < 2 && IS_ENUM(ufunc->uf_class))
+                   // The first two object variables in an enum are the name
+                   // and the ordinal.  These are set by the ISN_CONSTRUCT
+                   // instruction.  So don't generate instructions to set
+                   // these variables.
+                   continue;
+
+               if (m->ocm_init != NULL)
+               {
+                   char_u *expr = m->ocm_init;
+                   if (compile_expr0(&expr, &cctx) == FAIL)
+                       goto erret;
+                   if (!ends_excmd2(m->ocm_init, expr))
+                   {
+                       semsg(_(e_trailing_characters_str), expr);
+                       goto erret;
+                   }
+
+                   type_T      *type = get_type_on_stack(&cctx, 0);
+                   if (m->ocm_type->tt_type == VAR_ANY
+                           && !(m->ocm_flags & OCMFLAG_HAS_TYPE)
+                           && type->tt_type != VAR_SPECIAL)
+                   {
+                       // If the member variable type is not yet set, then use
+                       // the initialization expression type.
+                       m->ocm_type = type;
+                   }
+                   else if (m->ocm_type->tt_type != type->tt_type)
+                   {
+                       // The type of the member initialization expression is
+                       // determined at run time.  Add a runtime type check.
+                       where_T where = WHERE_INIT;
+                       where.wt_kind = WT_MEMBER;
+                       where.wt_func_name = (char *)m->ocm_name;
+                       if (need_type_where(type, m->ocm_type, FALSE, -1,
+                                   where, &cctx, FALSE, FALSE) == FAIL)
+                           goto erret;
+                   }
+               }
+               else
+                   push_default_value(&cctx, m->ocm_type->tt_type,
+                                                                 FALSE, NULL);
+               generate_STORE_THIS(&cctx, i);
+           }
+       }
+    }
+
+    if (ufunc->uf_def_args.ga_len > 0)
+    {
+       int     count = ufunc->uf_def_args.ga_len;
+       int     first_def_arg = ufunc->uf_args.ga_len - count;
+       int     i;
+       int     off = STACK_FRAME_SIZE + (ufunc->uf_va_name != NULL ? 1 : 0);
+       int     did_set_arg_type = FALSE;
+
+       // Produce instructions for the default values of optional arguments.
+       SOURCING_LNUM = 0;  // line number unknown
+       for (i = 0; i < count; ++i)
+       {
+           char_u *arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
+           if (STRCMP(arg, "v:none") == 0)
+               // "arg = v:none" means the argument is optional without
+               // setting a value when the argument is missing.
+               continue;
+
+           type_T      *val_type;
+           int         arg_idx = first_def_arg + i;
+           where_T     where = WHERE_INIT;
+           int         jump_instr_idx = instr->ga_len;
+           isn_T       *isn;
+
+           // Use a JUMP_IF_ARG_SET instruction to skip if the value was given.
+           if (generate_JUMP_IF_ARG(&cctx, ISN_JUMP_IF_ARG_SET,
+                                                     i - count - off) == FAIL)
+               goto erret;
+
+           // Make sure later arguments are not found.
+           ufunc->uf_args_visible = arg_idx;
+
+           int r = compile_expr0(&arg, &cctx);
+           if (r == FAIL)
+               goto erret;
+
+           // If no type specified use the type of the default value.
+           // Otherwise check that the default value type matches the
+           // specified type.
+           val_type = get_type_on_stack(&cctx, 0);
+           where.wt_index = arg_idx + 1;
+           where.wt_kind = WT_ARGUMENT;
+           if (ufunc->uf_arg_types[arg_idx] == &t_unknown)
+           {
+               did_set_arg_type = TRUE;
+               ufunc->uf_arg_types[arg_idx] = val_type;
+           }
+           else if (need_type_where(val_type, ufunc->uf_arg_types[arg_idx],
+                               FALSE, -1, where, &cctx, FALSE, FALSE) == FAIL)
+               goto erret;
+
+           if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL)
+               goto erret;
+
+           // set instruction index in JUMP_IF_ARG_SET to here
+           isn = ((isn_T *)instr->ga_data) + jump_instr_idx;
+           isn->isn_arg.jumparg.jump_where = instr->ga_len;
+       }
+
+       if (did_set_arg_type)
+           set_function_type(ufunc);
+    }
+    ufunc->uf_args_visible = ufunc->uf_args.ga_len;
+
+    // Compiling a function in an interface is done to get the function type.
+    // No code is actually compiled.
+    if (ufunc->uf_class != NULL && IS_INTERFACE(ufunc->uf_class))
+    {
+       ufunc->uf_def_status = UF_NOT_COMPILED;
+       ret = OK;
+       goto erret;
+    }
+
+    if (compile_def_function_body(&cctx, ufunc->uf_lines.ga_len,
+               check_return_type, &lines_to_free, &errormsg) == FAIL)
+       goto erret;
+
     if (cctx.ctx_scope != NULL)
     {
        if (cctx.ctx_scope->se_type == IF_SCOPE)
diff --git a/src/vim9type.c b/src/vim9type.c
index 228df6e2f..775291e74 100644
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -1894,14 +1894,12 @@ type_name_list_or_dict(char *name, type_T *type, char 
**tofree)
 
     size_t len = STRLEN(name) + STRLEN(member_name) + 3;
     *tofree = alloc(len);
-    if (*tofree != NULL)
-    {
-       vim_snprintf(*tofree, len, "%s<%s>", name, member_name);
-       vim_free(member_free);
-       return *tofree;
-    }
+    if (*tofree == NULL)
+       return name;
 
-    return name;
+    vim_snprintf(*tofree, len, "%s<%s>", name, member_name);
+    vim_free(member_free);
+    return *tofree;
 }
 
 /*
@@ -1924,17 +1922,15 @@ type_name_class_or_obj(char *name, type_T *type, char 
**tofree)
 
     size_t len = STRLEN(name) + STRLEN(class_name) + 3;
     *tofree = alloc(len);
-    if (*tofree != NULL)
-    {
-       vim_snprintf(*tofree, len, "%s<%s>", name, class_name);
-       return *tofree;
-    }
+    if (*tofree == NULL)
+       return name;
 
-    return name;
+    vim_snprintf(*tofree, len, "%s<%s>", name, class_name);
+    return *tofree;
 }
 
 /*
- * Return the type name of a functio.
+ * Return the type name of a function.
  * The result may be in allocated memory, in which case "tofree" is set.
  */
     static char *

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1rzKuA-00AhIv-4J%40256bit.org.

Raspunde prin e-mail lui