summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2024-04-23 20:14:46 +0200
committerChristian Brabandt <cb@256bit.org>2024-04-23 20:14:46 +0200
commita16f251333e324c94ca8e3e92d1fcf3193dfa382 (patch)
treed09dde5c33b8e1612588e8c97f9143f9a8fd5309
parentea999037a41292b3d3e00700a87a82fe5d2c12b2 (diff)
patch 9.1.0367: compile_def_function is too longv9.1.0367
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 <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
-rw-r--r--src/testdir/test_vim9_builtin.vim2
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c704
-rw-r--r--src/vim9type.c24
4 files changed, 375 insertions, 357 deletions
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 35ff091a45..42d09fe9a5 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 7113699f3a..fc3969164a 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 367,
+/**/
366,
/**/
365,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 0e05f820c5..a091c57963 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, '\n') == 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 228df6e2fa..775291e743 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);
+